prebid.js 8.7.0 → 8.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/dist/33acrossBidAdapter.js +1 -1
  2. package/dist/33acrossIdSystem.js +1 -1
  3. package/dist/a1MediaRtdProvider.js +1 -1
  4. package/dist/adagioBidAdapter.js +1 -1
  5. package/dist/adbookpspBidAdapter.js +1 -1
  6. package/dist/adgenerationBidAdapter.js +1 -1
  7. package/dist/admaticBidAdapter.js +1 -1
  8. package/dist/adqueryBidAdapter.js +1 -1
  9. package/dist/adqueryIdSystem.js +1 -1
  10. package/dist/adrelevantisBidAdapter.js +1 -1
  11. package/dist/adtrgtmeBidAdapter.js +1 -1
  12. package/dist/adxcgBidAdapter.js +1 -1
  13. package/dist/adyoulikeBidAdapter.js +1 -1
  14. package/dist/ajaBidAdapter.js +1 -1
  15. package/dist/amxBidAdapter.js +1 -1
  16. package/dist/amxIdSystem.js +1 -1
  17. package/dist/appierAnalyticsAdapter.js +1 -1
  18. package/dist/appnexusBidAdapter.js +1 -1
  19. package/dist/asoBidAdapter.js +1 -1
  20. package/dist/axonixBidAdapter.js +1 -1
  21. package/dist/bidViewability.js +1 -1
  22. package/dist/bidglassBidAdapter.js +1 -1
  23. package/dist/big-richmediaBidAdapter.js +1 -1
  24. package/dist/brandmetricsRtdProvider.js +1 -1
  25. package/dist/bridBidAdapter.js +1 -1
  26. package/dist/bridgewellBidAdapter.js +1 -1
  27. package/dist/brightMountainMediaBidAdapter.js +1 -1
  28. package/dist/carodaBidAdapter.js +1 -1
  29. package/dist/chtnwBidAdapter.js +1 -1
  30. package/dist/concertBidAdapter.js +1 -1
  31. package/dist/connectadBidAdapter.js +1 -1
  32. package/dist/consumableBidAdapter.js +1 -1
  33. package/dist/conversantAnalyticsAdapter.js +1 -1
  34. package/dist/conversantBidAdapter.js +1 -1
  35. package/dist/craftBidAdapter.js +1 -1
  36. package/dist/criteoBidAdapter.js +1 -1
  37. package/dist/cwireBidAdapter.js +1 -1
  38. package/dist/dependencies.json +4 -1
  39. package/dist/dspxBidAdapter.js +1 -1
  40. package/dist/eplanningBidAdapter.js +1 -1
  41. package/dist/euidIdSystem.js +1 -1
  42. package/dist/feedadBidAdapter.js +1 -1
  43. package/dist/finativeBidAdapter.js +1 -1
  44. package/dist/freewheel-sspBidAdapter.js +1 -1
  45. package/dist/geoedgeRtdProvider.js +1 -1
  46. package/dist/gmosspBidAdapter.js +1 -1
  47. package/dist/goldbachBidAdapter.js +1 -1
  48. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  49. package/dist/greenbidsRtdProvider.js +1 -1
  50. package/dist/gridBidAdapter.js +1 -1
  51. package/dist/gumgumBidAdapter.js +1 -1
  52. package/dist/h12mediaBidAdapter.js +1 -1
  53. package/dist/hypelabBidAdapter.js +1 -1
  54. package/dist/id5IdSystem.js +1 -1
  55. package/dist/imdsBidAdapter.js +1 -1
  56. package/dist/improvedigitalBidAdapter.js +1 -1
  57. package/dist/insticatorBidAdapter.js +1 -1
  58. package/dist/ixBidAdapter.js +1 -1
  59. package/dist/justpremiumBidAdapter.js +1 -1
  60. package/dist/kargoBidAdapter.js +1 -1
  61. package/dist/konduitAnalyticsAdapter.js +1 -1
  62. package/dist/kueezBidAdapter.js +1 -1
  63. package/dist/kueezRtbBidAdapter.js +1 -1
  64. package/dist/kulturemediaBidAdapter.js +1 -1
  65. package/dist/lassoBidAdapter.js +1 -1
  66. package/dist/lifestreetBidAdapter.js +1 -1
  67. package/dist/liveIntentIdSystem.js +1 -1
  68. package/dist/logicadBidAdapter.js +1 -1
  69. package/dist/loglyliftBidAdapter.js +1 -1
  70. package/dist/magniteAnalyticsAdapter.js +1 -1
  71. package/dist/malltvAnalyticsAdapter.js +1 -1
  72. package/dist/marsmediaBidAdapter.js +1 -1
  73. package/dist/mediafuseBidAdapter.js +1 -1
  74. package/dist/mediasquareBidAdapter.js +1 -1
  75. package/dist/mgidBidAdapter.js +1 -1
  76. package/dist/minutemediaBidAdapter.js +1 -1
  77. package/dist/minutemediaplusBidAdapter.js +1 -1
  78. package/dist/nativoBidAdapter.js +1 -1
  79. package/dist/nexx360BidAdapter.js +1 -1
  80. package/dist/not-for-prod/prebid.js +138 -137
  81. package/dist/oguryBidAdapter.js +1 -1
  82. package/dist/onetagBidAdapter.js +1 -1
  83. package/dist/ooloAnalyticsAdapter.js +1 -1
  84. package/dist/operaadsBidAdapter.js +1 -1
  85. package/dist/operaadsIdSystem.js +1 -0
  86. package/dist/optidigitalBidAdapter.js +1 -1
  87. package/dist/outbrainBidAdapter.js +1 -1
  88. package/dist/oxxionAnalyticsAdapter.js +1 -1
  89. package/dist/parrableIdSystem.js +1 -1
  90. package/dist/pixfutureBidAdapter.js +1 -1
  91. package/dist/prebid-core.js +1 -1
  92. package/dist/publinkIdSystem.js +1 -1
  93. package/dist/pubmaticBidAdapter.js +1 -1
  94. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  95. package/dist/pxyzBidAdapter.js +1 -1
  96. package/dist/quantcastBidAdapter.js +1 -1
  97. package/dist/readpeakBidAdapter.js +1 -1
  98. package/dist/relaidoBidAdapter.js +1 -1
  99. package/dist/retailspotBidAdapter.js +1 -1
  100. package/dist/rhythmoneBidAdapter.js +1 -1
  101. package/dist/riseBidAdapter.js +1 -1
  102. package/dist/rubiconBidAdapter.js +1 -1
  103. package/dist/seedingAllianceBidAdapter.js +1 -1
  104. package/dist/seedtagBidAdapter.js +1 -1
  105. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  106. package/dist/sharethroughBidAdapter.js +1 -1
  107. package/dist/shinezBidAdapter.js +1 -1
  108. package/dist/smaatoBidAdapter.js +1 -1
  109. package/dist/smartadserverBidAdapter.js +1 -1
  110. package/dist/smartxBidAdapter.js +1 -1
  111. package/dist/smartyadsBidAdapter.js +1 -1
  112. package/dist/smilewantedBidAdapter.js +1 -1
  113. package/dist/snigelBidAdapter.js +1 -1
  114. package/dist/sonobiBidAdapter.js +1 -1
  115. package/dist/sovrnAnalyticsAdapter.js +1 -1
  116. package/dist/sovrnBidAdapter.js +1 -1
  117. package/dist/sspBCBidAdapter.js +1 -1
  118. package/dist/stvBidAdapter.js +1 -1
  119. package/dist/sublimeBidAdapter.js +1 -1
  120. package/dist/targetVideoBidAdapter.js +1 -1
  121. package/dist/teadsBidAdapter.js +1 -1
  122. package/dist/topicsFpdModule.js +1 -1
  123. package/dist/trionBidAdapter.js +1 -1
  124. package/dist/tripleliftBidAdapter.js +1 -1
  125. package/dist/ttdBidAdapter.js +1 -1
  126. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  127. package/dist/uid2IdSystem.js +1 -1
  128. package/dist/underdogmediaBidAdapter.js +1 -1
  129. package/dist/undertoneBidAdapter.js +1 -1
  130. package/dist/vidazooBidAdapter.js +1 -1
  131. package/dist/videobyteBidAdapter.js +1 -1
  132. package/dist/visxBidAdapter.js +1 -1
  133. package/dist/vuukleBidAdapter.js +1 -1
  134. package/dist/widespaceBidAdapter.js +1 -1
  135. package/dist/winrBidAdapter.js +1 -1
  136. package/dist/yahoosspBidAdapter.js +1 -1
  137. package/dist/yieldmoBidAdapter.js +1 -1
  138. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  139. package/integrationExamples/topics/topics-server.js +72 -0
  140. package/modules/.submodules.json +2 -1
  141. package/modules/a1MediaRtdProvider.js +1 -1
  142. package/modules/admaticBidAdapter.js +5 -0
  143. package/modules/adqueryBidAdapter.js +23 -21
  144. package/modules/adqueryIdSystem.js +45 -30
  145. package/modules/adrelevantisBidAdapter.js +2 -0
  146. package/modules/bidViewability.js +7 -0
  147. package/modules/bidViewability.md +9 -8
  148. package/modules/brandmetricsRtdProvider.js +30 -27
  149. package/modules/criteoBidAdapter.js +66 -43
  150. package/modules/geoedgeRtdProvider.js +43 -13
  151. package/modules/geoedgeRtdProvider.md +2 -1
  152. package/modules/liveIntentIdSystem.js +16 -1
  153. package/modules/nativoBidAdapter.js +21 -3
  154. package/modules/onetagBidAdapter.js +3 -0
  155. package/modules/operaadsBidAdapter.js +5 -0
  156. package/modules/operaadsBidAdapter.md +5 -5
  157. package/modules/operaadsIdSystem.js +106 -0
  158. package/modules/operaadsIdSystem.md +52 -0
  159. package/modules/oxxionAnalyticsAdapter.js +22 -5
  160. package/modules/rubiconBidAdapter.js +2 -2
  161. package/modules/smartyadsBidAdapter.js +18 -1
  162. package/modules/snigelBidAdapter.js +69 -16
  163. package/modules/snigelBidAdapter.md +9 -3
  164. package/modules/topicsFpdModule.js +35 -9
  165. package/modules/visxBidAdapter.js +51 -1
  166. package/package.json +2 -2
  167. package/src/adloader.js +1 -0
  168. package/src/native.js +2 -2
  169. package/test/spec/modules/admaticBidAdapter_spec.js +5 -0
  170. package/test/spec/modules/adqueryIdSystem_spec.js +13 -24
  171. package/test/spec/modules/bidViewability_spec.js +32 -1
  172. package/test/spec/modules/brandmetricsRtdProvider_spec.js +6 -0
  173. package/test/spec/modules/criteoBidAdapter_spec.js +228 -0
  174. package/test/spec/modules/eids_spec.js +48 -0
  175. package/test/spec/modules/geoedgeRtdProvider_spec.js +44 -24
  176. package/test/spec/modules/liveIntentIdMinimalSystem_spec.js +5 -0
  177. package/test/spec/modules/liveIntentIdSystem_spec.js +5 -0
  178. package/test/spec/modules/nativoBidAdapter_spec.js +6 -1
  179. package/test/spec/modules/onetagBidAdapter_spec.js +69 -0
  180. package/test/spec/modules/operaadsIdSystem_spec.js +53 -0
  181. package/test/spec/modules/oxxionAnalyticsAdapter_spec.js +6 -1
  182. package/test/spec/modules/rubiconBidAdapter_spec.js +24 -0
  183. package/test/spec/modules/smartyadsBidAdapter_spec.js +49 -0
  184. package/test/spec/modules/snigelBidAdapter_spec.js +57 -6
  185. package/test/spec/modules/topicsFpdModule_spec.js +102 -1
  186. package/test/spec/modules/visxBidAdapter_spec.js +151 -1
  187. package/test/spec/native_spec.js +5 -3
@@ -0,0 +1,72 @@
1
+ // This is an example of a server-side endpoint that is utilizing the Topics API header functionality.
2
+ // Note: This test endpoint requires the following to run: node.js, npm, express, cors, body-parser
3
+
4
+ const bodyParser = require('body-parser');
5
+ const cors = require('cors');
6
+ const express = require('express');
7
+
8
+ const port = process.env.PORT || 3000;
9
+
10
+ const app = express();
11
+ app.use(cors());
12
+ app.use(
13
+ bodyParser.urlencoded({
14
+ extended: true,
15
+ })
16
+ );
17
+ app.use(bodyParser.json());
18
+ app.use(express.static('public'));
19
+ app.set('port', port);
20
+
21
+ const listener = app.listen(port, () => {
22
+ const host =
23
+ listener.address().address === '::'
24
+ ? 'http://localhost'
25
+ : 'http://' + listener.address().address;
26
+ // eslint-disable-next-line no-console
27
+ console.log(
28
+ `${__filename} is listening on ${host}:${listener.address().port}\n`
29
+ );
30
+ });
31
+
32
+ app.get('*', (req, res) => {
33
+ res.setHeader('Observe-Browsing-Topics', '?1');
34
+
35
+ const resData = {
36
+ segment: {
37
+ domain: req.hostname,
38
+ topics: generateTopicArrayFromHeader(req.headers['sec-browsing-topics']),
39
+ bidder: req.query['bidder'],
40
+ },
41
+ date: Date.now(),
42
+ };
43
+
44
+ res.json(resData);
45
+ });
46
+
47
+ const generateTopicArrayFromHeader = (topicString) => {
48
+ const result = [];
49
+ const topicArray = topicString.split(', ');
50
+ if (topicArray.length > 1) {
51
+ topicArray.pop();
52
+ topicArray.map((topic) => {
53
+ const topicId = topic.split(';')[0];
54
+ const versionsString = topic.split(';')[1].split('=')[1];
55
+ const [config, taxonomy, model] = versionsString.split(':');
56
+ const numTopicsWithSameVersions = topicId
57
+ .substring(1, topicId.length - 1)
58
+ .split(' ');
59
+
60
+ numTopicsWithSameVersions.map((tpId) => {
61
+ result.push({
62
+ topic: tpId,
63
+ version: versionsString,
64
+ configVersion: config,
65
+ taxonomyVersion: taxonomy,
66
+ modelVersion: model,
67
+ });
68
+ });
69
+ });
70
+ }
71
+ return result;
72
+ };
@@ -46,7 +46,8 @@
46
46
  "zeotapIdPlusIdSystem",
47
47
  "adqueryIdSystem",
48
48
  "gravitoIdSystem",
49
- "freepassIdSystem"
49
+ "freepassIdSystem",
50
+ "operaadsIdSystem"
50
51
  ],
51
52
  "adpod": [
52
53
  "freeWheelAdserverVideo",
@@ -8,7 +8,7 @@ const REAL_TIME_MODULE = 'realTimeData';
8
8
  const MODULE_NAME = 'a1Media';
9
9
  const SCRIPT_URL = 'https://linkback.contentsfeed.com/src';
10
10
  export const A1_SEG_KEY = '__a1tg';
11
- export const A1_AUD_KEY = 'a1gid';
11
+ export const A1_AUD_KEY = 'a1_gid';
12
12
 
13
13
  export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME});
14
14
 
@@ -195,6 +195,11 @@ function buildRequestObject(bid) {
195
195
  reqObj.type = 'video';
196
196
  reqObj.mediatype = bid.mediaTypes.video;
197
197
  }
198
+
199
+ if (deepAccess(bid, 'ortb2Imp.ext')) {
200
+ reqObj.ext = bid.ortb2Imp.ext;
201
+ }
202
+
198
203
  reqObj.id = getBidIdParameter('bidId', bid);
199
204
 
200
205
  enrichSlotWithFloors(reqObj, bid);
@@ -1,7 +1,6 @@
1
1
  import {registerBidder} from '../src/adapters/bidderFactory.js';
2
2
  import {BANNER} from '../src/mediaTypes.js';
3
- import { logInfo, buildUrl, triggerPixel, parseSizesInput } from '../src/utils.js';
4
- import { getStorageManager } from '../src/storageManager.js';
3
+ import {buildUrl, logInfo, parseSizesInput, triggerPixel} from '../src/utils.js';
5
4
 
6
5
  const ADQUERY_GVLID = 902;
7
6
  const ADQUERY_BIDDER_CODE = 'adquery';
@@ -11,7 +10,6 @@ const ADQUERY_USER_SYNC_DOMAIN = ADQUERY_BIDDER_DOMAIN_PROTOCOL + '://' + ADQUER
11
10
  const ADQUERY_DEFAULT_CURRENCY = 'PLN';
12
11
  const ADQUERY_NET_REVENUE = true;
13
12
  const ADQUERY_TTL = 360;
14
- const storage = getStorageManager({bidderCode: ADQUERY_BIDDER_CODE});
15
13
 
16
14
  /** @type {BidderSpec} */
17
15
  export const spec = {
@@ -55,10 +53,6 @@ export const spec = {
55
53
  * @return {Bid[]}
56
54
  */
57
55
  interpretResponse: (response, request) => {
58
- logInfo(request);
59
- logInfo(response);
60
-
61
- let qid = null;
62
56
  const res = response && response.body && response.body.data;
63
57
  let bidResponses = [];
64
58
 
@@ -87,17 +81,6 @@ export const spec = {
87
81
  bidResponses.push(bidResponse);
88
82
  logInfo('bidResponses', bidResponses);
89
83
 
90
- if (res && res.qid) {
91
- if (storage.getDataFromLocalStorage('qid')) {
92
- qid = storage.getDataFromLocalStorage('qid');
93
- if (qid && qid.includes('%7B%22')) {
94
- storage.setDataInLocalStorage('qid', res.qid);
95
- }
96
- } else {
97
- storage.setDataInLocalStorage('qid', res.qid);
98
- }
99
- }
100
-
101
84
  return bidResponses;
102
85
  },
103
86
 
@@ -189,8 +172,28 @@ export const spec = {
189
172
  }
190
173
 
191
174
  };
175
+
192
176
  function buildRequest(validBidRequests, bidderRequest) {
193
177
  let bid = validBidRequests;
178
+ logInfo('buildRequest: ', bid);
179
+
180
+ let userId = null;
181
+ if (window.qid) {
182
+ userId = window.qid;
183
+ }
184
+
185
+ if (bid.userId && bid.userId.qid) {
186
+ userId = bid.userId.qid
187
+ }
188
+
189
+ if (!userId) {
190
+ // onetime User ID
191
+ const randomValues = Array.from(window.crypto.getRandomValues(new Uint32Array(4)));
192
+ userId = randomValues.map(it => it.toString(36)).join().substring(20);
193
+
194
+ window.qid = userId;
195
+ }
196
+
194
197
  let pageUrl = '';
195
198
  if (bidderRequest && bidderRequest.refererInfo) {
196
199
  pageUrl = bidderRequest.refererInfo.page || '';
@@ -199,11 +202,10 @@ function buildRequest(validBidRequests, bidderRequest) {
199
202
  return {
200
203
  v: '$prebid.version$',
201
204
  placementCode: bid.params.placementId,
202
- // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781
203
- auctionId: bid.auctionId,
205
+ auctionId: null,
204
206
  type: bid.params.type,
205
207
  adUnitCode: bid.adUnitCode,
206
- bidQid: storage.getDataFromLocalStorage('qid') || null,
208
+ bidQid: userId,
207
209
  bidId: bid.bidId,
208
210
  bidder: bid.bidder,
209
211
  bidPageUrl: pageUrl,
@@ -8,7 +8,7 @@
8
8
  import {ajax} from '../src/ajax.js';
9
9
  import {getStorageManager} from '../src/storageManager.js';
10
10
  import {submodule} from '../src/hook.js';
11
- import { isFn, isStr, isPlainObject, logError } from '../src/utils.js';
11
+ import {isFn, isPlainObject, isStr, logError, logInfo} from '../src/utils.js';
12
12
  import {MODULE_TYPE_UID} from '../src/activities/modules.js';
13
13
 
14
14
  const MODULE_NAME = 'qid';
@@ -51,11 +51,7 @@ export const adqueryIdSubmodule = {
51
51
  * @returns {{qid:Object}}
52
52
  */
53
53
  decode(value) {
54
- let qid = storage.getDataFromLocalStorage('qid');
55
- if (isStr(qid)) {
56
- return {qid: qid};
57
- }
58
- return (value && typeof value['qid'] === 'string') ? { 'qid': value['qid'] } : undefined;
54
+ return {qid: value}
59
55
  },
60
56
  /**
61
57
  * performs action to obtain id and return a value in the callback's response argument
@@ -64,38 +60,57 @@ export const adqueryIdSubmodule = {
64
60
  * @returns {IdResponse|undefined}
65
61
  */
66
62
  getId(config) {
63
+ logInfo('adqueryIdSubmodule getId');
67
64
  if (!isPlainObject(config.params)) {
68
65
  config.params = {};
69
66
  }
70
- const url = paramOrDefault(config.params.url,
67
+
68
+ const url = paramOrDefault(
69
+ config.params.url,
71
70
  `https://bidder.adquery.io/prebid/qid`,
72
- config.params.urlArg);
71
+ config.params.urlArg
72
+ );
73
73
 
74
74
  const resp = function (callback) {
75
- let qid = storage.getDataFromLocalStorage('qid');
76
- if (isStr(qid)) {
77
- const responseObj = {qid: qid};
78
- callback(responseObj);
79
- } else {
80
- const callbacks = {
81
- success: response => {
82
- let responseObj;
83
- if (response) {
84
- try {
85
- responseObj = JSON.parse(response);
86
- } catch (error) {
87
- logError(error);
88
- }
75
+ let qid = window.qid;
76
+
77
+ if (!qid) {
78
+ const ramdomValues = window.crypto.getRandomValues(new Uint32Array(4));
79
+ qid = (ramdomValues[0].toString(36) +
80
+ ramdomValues[1].toString(36) +
81
+ ramdomValues[2].toString(36) +
82
+ ramdomValues[3].toString(36))
83
+ .substring(0, 20);
84
+
85
+ const randomValues = Array.from(window.crypto.getRandomValues(new Uint32Array(4)));
86
+ qid = randomValues.map(it => it.toString(36)).join().substring(20);
87
+ logInfo('adqueryIdSubmodule ID QID GENERTAED:', qid);
88
+ }
89
+ logInfo('adqueryIdSubmodule ID QID:', qid);
90
+
91
+ const callbacks = {
92
+ success: response => {
93
+ let responseObj;
94
+ if (response) {
95
+ try {
96
+ responseObj = JSON.parse(response);
97
+ } catch (error) {
98
+ logError(error);
89
99
  }
90
- callback(responseObj);
91
- },
92
- error: error => {
93
- logError(`${MODULE_NAME}: ID fetch encountered an error`, error);
94
- callback();
95
100
  }
96
- };
97
- ajax(url, callbacks, undefined, {method: 'GET'});
98
- }
101
+ if (responseObj.qid) {
102
+ let myQid = responseObj.qid;
103
+ storage.setDataInLocalStorage('qid', myQid);
104
+ return callback(myQid);
105
+ }
106
+ callback();
107
+ },
108
+ error: error => {
109
+ logError(`${MODULE_NAME}: ID fetch encountered an error`, error);
110
+ callback();
111
+ }
112
+ };
113
+ ajax(url + '?qid=' + qid, callbacks, undefined, {method: 'GET'});
99
114
  };
100
115
  return {callback: resp};
101
116
  },
@@ -597,6 +597,8 @@ function parseMediaType(rtbBid) {
597
597
  const adType = rtbBid.ad_type;
598
598
  if (adType === VIDEO) {
599
599
  return VIDEO;
600
+ } else if (adType === NATIVE) {
601
+ return NATIVE;
600
602
  } else {
601
603
  return BANNER;
602
604
  }
@@ -67,6 +67,8 @@ export let logWinningBidNotFound = (slot) => {
67
67
 
68
68
  export let impressionViewableHandler = (globalModuleConfig, slot, event) => {
69
69
  let respectiveBid = getMatchingWinningBidForGPTSlot(globalModuleConfig, slot);
70
+ let respectiveDeferredAdUnit = getGlobal().adUnits.find(adUnit => adUnit.deferBilling && respectiveBid.adUnitCode === adUnit.code);
71
+
70
72
  if (respectiveBid === null) {
71
73
  logWinningBidNotFound(slot);
72
74
  } else {
@@ -74,6 +76,11 @@ export let impressionViewableHandler = (globalModuleConfig, slot, event) => {
74
76
  fireViewabilityPixels(globalModuleConfig, respectiveBid);
75
77
  // trigger respective bidder's onBidViewable handler
76
78
  adapterManager.callBidViewableBidder(respectiveBid.adapterCode || respectiveBid.bidder, respectiveBid);
79
+
80
+ if (respectiveDeferredAdUnit) {
81
+ adapterManager.callBidBillableBidder(respectiveBid);
82
+ }
83
+
77
84
  // emit the BID_VIEWABLE event with bid details, this event can be consumed by bidders and analytics pixels
78
85
  events.emit(CONSTANTS.EVENTS.BID_VIEWABLE, respectiveBid);
79
86
  }
@@ -2,19 +2,20 @@
2
2
 
3
3
  Module Name: bidViewability
4
4
 
5
- Purpose: Track when a bid is viewable
5
+ Purpose: Track when a bid is viewable (and also ready for billing)
6
6
 
7
7
  Maintainer: harshad.mane@pubmatic.com
8
8
 
9
9
  # Description
10
- - This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Analytics adapters, bidders will need to implement `onBidViewable` method to capture this event
11
- - Bidderes can check if this module is part of the final build and whether it is enabled or not by accessing ```pbjs.getConfig('bidViewability')```
10
+ - This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Analytics adapters, bidders will need to implement the `onBidViewable` method to capture this event
11
+ - Bidders can check if this module is part of the final build and whether it is enabled or not by accessing ```pbjs.getConfig('bidViewability')```
12
12
  - GPT API is used to find when a bid is viewable, https://developers.google.com/publisher-tag/reference#googletag.events.impressionviewableevent . This event is fired when an impression becomes viewable, according to the Active View criteria.
13
13
  Refer: https://support.google.com/admanager/answer/4524488
14
- - The module does not work with adserver other than GAM with GPT integration
14
+ - This module does not work with any adserver's other than GAM with GPT integration
15
15
  - Logic used to find a matching pbjs-bid for a GPT slot is ``` (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode) ``` this logic can be changed by using param ```customMatchFunction```
16
- - When a rendered PBJS bid is viewable the module will trigger BID_VIEWABLE event, which can be consumed by bidders and analytics adapters
17
- - For the viewable bid if ```bid.vurls type array``` param is and module config ``` firePixels: true ``` is set then the URLs mentioned in bid.vurls will be executed. Please note that GDPR and USP related parameters will be added to the given URLs
16
+ - When a rendered PBJS bid is viewable the module will trigger a BID_VIEWABLE event, which can be consumed by bidders and analytics adapters
17
+ - If the viewable bid contains a ```vurls``` param containing URL's and the Bid Viewability module is configured with ``` firePixels: true ``` then the URLs mentioned in bid.vurls will be called. Please note that GDPR and USP related parameters will be added to the given URLs
18
+ - This module is also compatible with Prebid core's billing deferral logic, this means that bids linked to an ad unit marked with `deferBilling: true` will trigger a bid adapter's `onBidBillable` function (if present) indicating an ad slot was viewed and also billing ready (if it were deferred).
18
19
 
19
20
  # Params
20
21
  - enabled [required] [type: boolean, default: false], when set to true, the module will emit BID_VIEWABLE when applicable
@@ -44,6 +45,6 @@ Refer: https://support.google.com/admanager/answer/4524488
44
45
  ```
45
46
 
46
47
  # Please Note:
47
- - Doesn't seems to work with Instream Video, https://docs.prebid.org/dev-docs/examples/instream-banner-mix.html as GPT's impressionViewable event is not triggered for instream-video-creative
48
- - Works with Banner, Outsteam, Native creatives
48
+ - This module doesn't seem to work with Instream Video, https://docs.prebid.org/dev-docs/examples/instream-banner-mix.html as GPT's impressionViewable event is not triggered for instream-video-creative
49
+ - Works with Banner, Outsteam and Native creatives
49
50
 
@@ -21,13 +21,14 @@ let billableEventsInitialized = false
21
21
 
22
22
  function init (config, userConsent) {
23
23
  const hasConsent = checkConsent(userConsent)
24
+ const initialize = hasConsent !== false
24
25
 
25
- if (hasConsent) {
26
+ if (initialize) {
26
27
  const moduleConfig = getMergedConfig(config)
27
28
  initializeBrandmetrics(moduleConfig.params.scriptId)
28
29
  initializeBillableEvents()
29
30
  }
30
- return hasConsent
31
+ return initialize
31
32
  }
32
33
 
33
34
  /**
@@ -36,33 +37,35 @@ function init (config, userConsent) {
36
37
  * @returns {boolean}
37
38
  */
38
39
  function checkConsent (userConsent) {
39
- let consent = false
40
-
41
- if (userConsent && userConsent.gdpr && userConsent.gdpr.gdprApplies) {
42
- const gdpr = userConsent.gdpr
43
-
44
- if (gdpr.vendorData) {
45
- const vendor = gdpr.vendorData.vendor
46
- const purpose = gdpr.vendorData.purpose
47
-
48
- let vendorConsent = false
49
- if (vendor.consents) {
50
- vendorConsent = vendor.consents[GVL_ID]
40
+ let consent
41
+
42
+ if (userConsent) {
43
+ if (userConsent.gdpr && userConsent.gdpr.gdprApplies) {
44
+ const gdpr = userConsent.gdpr
45
+
46
+ if (gdpr.vendorData) {
47
+ const vendor = gdpr.vendorData.vendor
48
+ const purpose = gdpr.vendorData.purpose
49
+
50
+ let vendorConsent = false
51
+ if (vendor.consents) {
52
+ vendorConsent = vendor.consents[GVL_ID]
53
+ }
54
+
55
+ if (vendor.legitimateInterests) {
56
+ vendorConsent = vendorConsent || vendor.legitimateInterests[GVL_ID]
57
+ }
58
+
59
+ const purposes = TCF_PURPOSES.map(id => {
60
+ return (purpose.consents && purpose.consents[id]) || (purpose.legitimateInterests && purpose.legitimateInterests[id])
61
+ })
62
+ const purposesValid = purposes.filter(p => p === true).length === TCF_PURPOSES.length
63
+ consent = vendorConsent && purposesValid
51
64
  }
52
-
53
- if (vendor.legitimateInterests) {
54
- vendorConsent = vendorConsent || vendor.legitimateInterests[GVL_ID]
55
- }
56
-
57
- const purposes = TCF_PURPOSES.map(id => {
58
- return (purpose.consents && purpose.consents[id]) || (purpose.legitimateInterests && purpose.legitimateInterests[id])
59
- })
60
- const purposesValid = purposes.filter(p => p === true).length === TCF_PURPOSES.length
61
- consent = vendorConsent && purposesValid
65
+ } else if (userConsent.usp) {
66
+ const usp = userConsent.usp
67
+ consent = usp[1] !== 'N' && usp[2] !== 'Y'
62
68
  }
63
- } else if (userConsent.usp) {
64
- const usp = userConsent.usp
65
- consent = usp[1] !== 'N' && usp[2] !== 'Y'
66
69
  }
67
70
 
68
71
  return consent
@@ -3,7 +3,6 @@ import { loadExternalScript } from '../src/adloader.js';
3
3
  import { registerBidder } from '../src/adapters/bidderFactory.js';
4
4
  import { config } from '../src/config.js';
5
5
  import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
6
- import { find } from '../src/polyfill.js';
7
6
  import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; // ref#2
8
7
  import { getStorageManager } from '../src/storageManager.js';
9
8
  import { getRefererInfo } from '../src/refererDetection.js';
@@ -29,7 +28,7 @@ const LOG_PREFIX = 'Criteo: ';
29
28
  Unminified source code can be found in the privately shared repo: https://github.com/Prebid-org/prebid-js-external-js-criteo/blob/master/dist/prod.js
30
29
  */
31
30
  const FAST_BID_VERSION_PLACEHOLDER = '%FAST_BID_VERSION%';
32
- export const FAST_BID_VERSION_CURRENT = 136;
31
+ export const FAST_BID_VERSION_CURRENT = 139;
33
32
  const FAST_BID_VERSION_LATEST = 'latest';
34
33
  const FAST_BID_VERSION_NONE = 'none';
35
34
  const PUBLISHER_TAG_URL_TEMPLATE = 'https://static.criteo.net/js/ld/publishertag.prebid' + FAST_BID_VERSION_PLACEHOLDER + '.js';
@@ -219,51 +218,53 @@ export const spec = {
219
218
 
220
219
  if (body && body.slots && isArray(body.slots)) {
221
220
  body.slots.forEach(slot => {
222
- const bidRequest = find(request.bidRequests, b => b.adUnitCode === slot.impid && (!b.params.zoneId || parseInt(b.params.zoneId) === slot.zoneid));
223
- const bidId = bidRequest.bidId;
224
- const bid = {
225
- requestId: bidId,
226
- cpm: slot.cpm,
227
- currency: slot.currency,
228
- netRevenue: true,
229
- ttl: slot.ttl || 60,
230
- creativeId: slot.creativecode,
231
- width: slot.width,
232
- height: slot.height,
233
- dealId: slot.deal,
234
- };
235
- if (body.ext?.paf?.transmission && slot.ext?.paf?.content_id) {
236
- const pafResponseMeta = {
237
- content_id: slot.ext.paf.content_id,
238
- transmission: response.ext.paf.transmission
221
+ const bidRequest = getAssociatedBidRequest(request.bidRequests, slot);
222
+ if (bidRequest) {
223
+ const bidId = bidRequest.bidId;
224
+ const bid = {
225
+ requestId: bidId,
226
+ cpm: slot.cpm,
227
+ currency: slot.currency,
228
+ netRevenue: true,
229
+ ttl: slot.ttl || 60,
230
+ creativeId: slot.creativecode,
231
+ width: slot.width,
232
+ height: slot.height,
233
+ dealId: slot.deal,
239
234
  };
240
- bid.meta = Object.assign({}, bid.meta, { paf: pafResponseMeta });
241
- }
242
- if (slot.adomain) {
243
- bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [slot.adomain].flat() });
244
- }
245
- if (slot.ext?.meta?.networkName) {
246
- bid.meta = Object.assign({}, bid.meta, { networkName: slot.ext.meta.networkName })
247
- }
248
- if (slot.native) {
249
- if (bidRequest.params.nativeCallback) {
250
- bid.ad = createNativeAd(bidId, slot.native, bidRequest.params.nativeCallback);
251
- } else {
252
- bid.native = createPrebidNativeAd(slot.native);
253
- bid.mediaType = NATIVE;
235
+ if (body.ext?.paf?.transmission && slot.ext?.paf?.content_id) {
236
+ const pafResponseMeta = {
237
+ content_id: slot.ext.paf.content_id,
238
+ transmission: response.ext.paf.transmission
239
+ };
240
+ bid.meta = Object.assign({}, bid.meta, { paf: pafResponseMeta });
254
241
  }
255
- } else if (slot.video) {
256
- bid.vastUrl = slot.displayurl;
257
- bid.mediaType = VIDEO;
258
- const context = deepAccess(bidRequest, 'mediaTypes.video.context');
259
- // if outstream video, add a default render for it.
260
- if (context === OUTSTREAM) {
261
- bid.renderer = createOutstreamVideoRenderer(slot);
242
+ if (slot.adomain) {
243
+ bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [slot.adomain].flat() });
262
244
  }
263
- } else {
264
- bid.ad = slot.creative;
245
+ if (slot.ext?.meta?.networkName) {
246
+ bid.meta = Object.assign({}, bid.meta, { networkName: slot.ext.meta.networkName })
247
+ }
248
+ if (slot.native) {
249
+ if (bidRequest.params.nativeCallback) {
250
+ bid.ad = createNativeAd(bidId, slot.native, bidRequest.params.nativeCallback);
251
+ } else {
252
+ bid.native = createPrebidNativeAd(slot.native);
253
+ bid.mediaType = NATIVE;
254
+ }
255
+ } else if (slot.video) {
256
+ bid.vastUrl = slot.displayurl;
257
+ bid.mediaType = VIDEO;
258
+ const context = deepAccess(bidRequest, 'mediaTypes.video.context');
259
+ // if outstream video, add a default render for it.
260
+ if (context === OUTSTREAM) {
261
+ bid.renderer = createOutstreamVideoRenderer(slot);
262
+ }
263
+ } else {
264
+ bid.ad = slot.creative;
265
+ }
266
+ bids.push(bid);
265
267
  }
266
- bids.push(bid);
267
268
  });
268
269
  }
269
270
 
@@ -462,6 +463,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) {
462
463
  networkId = bidRequest.params.networkId || networkId;
463
464
  schain = bidRequest.schain || schain;
464
465
  const slot = {
466
+ slotid: bidRequest.bidId,
465
467
  impid: bidRequest.adUnitCode,
466
468
  transactionid: bidRequest.ortb2Imp?.ext?.tid
467
469
  };
@@ -786,6 +788,27 @@ function createOutstreamVideoRenderer(slot) {
786
788
  return renderer;
787
789
  }
788
790
 
791
+ function getAssociatedBidRequest(bidRequests, slot) {
792
+ for (const request of bidRequests) {
793
+ if (request.adUnitCode === slot.impid) {
794
+ if (request.params.zoneId && parseInt(request.params.zoneId) === slot.zoneid) {
795
+ return request;
796
+ } else if (slot.native) {
797
+ if (request.mediaTypes?.native || request.nativeParams) {
798
+ return request;
799
+ }
800
+ } else if (slot.video) {
801
+ if (request.mediaTypes?.video) {
802
+ return request;
803
+ }
804
+ } else if (request.mediaTypes?.banner || request.sizes) {
805
+ return request;
806
+ }
807
+ }
808
+ }
809
+ return undefined;
810
+ }
811
+
789
812
  export function tryGetCriteoFastBid() {
790
813
  // begin ref#1
791
814
  try {