prebid.js 9.6.0 → 9.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 (274) hide show
  1. package/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  2. package/.github/codeql/codeql-config.yml +3 -0
  3. package/.github/codeql/queries/deviceMemory.ql +14 -0
  4. package/.github/codeql/queries/hardwareConcurrency.ql +14 -0
  5. package/.github/codeql/queries/prebid.qll +36 -0
  6. package/.github/codeql/queries/qlpack.yml +8 -0
  7. package/.github/release-drafter.yml +4 -0
  8. package/.github/workflows/jscpd.yml +1 -1
  9. package/README.md +4 -4
  10. package/dist/33acrossAnalyticsAdapter.js +1 -1
  11. package/dist/33acrossBidAdapter.js +1 -1
  12. package/dist/33acrossIdSystem.js +1 -1
  13. package/dist/BTBidAdapter.js +1 -1
  14. package/dist/adagioAnalyticsAdapter.js +1 -1
  15. package/dist/adagioBidAdapter.js +1 -1
  16. package/dist/adagioUtils.js +1 -1
  17. package/dist/addefendBidAdapter.js +1 -1
  18. package/dist/adgenerationBidAdapter.js +1 -1
  19. package/dist/adlooxRtdProvider.js +1 -1
  20. package/dist/admixerBidAdapter.js +1 -1
  21. package/dist/adqueryBidAdapter.js +1 -1
  22. package/dist/adrelevantisBidAdapter.js +1 -1
  23. package/dist/adstirBidAdapter.js +1 -1
  24. package/dist/adtrgtmeBidAdapter.js +1 -1
  25. package/dist/adxcgAnalyticsAdapter.js +1 -1
  26. package/dist/adxcgBidAdapter.js +1 -1
  27. package/dist/adyoulikeBidAdapter.js +1 -1
  28. package/dist/agmaAnalyticsAdapter.js +1 -1
  29. package/dist/ajaBidAdapter.js +1 -1
  30. package/dist/amxBidAdapter.js +1 -1
  31. package/dist/amxIdSystem.js +1 -1
  32. package/dist/appierAnalyticsAdapter.js +1 -1
  33. package/dist/appnexusBidAdapter.js +1 -1
  34. package/dist/asoBidAdapter.js +1 -1
  35. package/dist/axonixBidAdapter.js +1 -1
  36. package/dist/bidglassBidAdapter.js +1 -1
  37. package/dist/big-richmediaBidAdapter.js +1 -1
  38. package/dist/bridBidAdapter.js +1 -1
  39. package/dist/bridgewellBidAdapter.js +1 -1
  40. package/dist/brightMountainMediaBidAdapter.js +1 -1
  41. package/dist/carodaBidAdapter.js +1 -1
  42. package/dist/ccxBidAdapter.js +1 -1
  43. package/dist/chtnwBidAdapter.js +1 -1
  44. package/dist/chunk-core.js +1 -1
  45. package/dist/cleanmedianetBidAdapter.js +1 -1
  46. package/dist/concertBidAdapter.js +1 -1
  47. package/dist/connectadBidAdapter.js +1 -1
  48. package/dist/connectionInfo.js +1 -0
  49. package/dist/consumableBidAdapter.js +1 -1
  50. package/dist/contxtfulRtdProvider.js +1 -1
  51. package/dist/conversantAnalyticsAdapter.js +1 -1
  52. package/dist/conversantBidAdapter.js +1 -1
  53. package/dist/craftBidAdapter.js +1 -1
  54. package/dist/criteoBidAdapter.js +1 -1
  55. package/dist/cwireBidAdapter.js +1 -1
  56. package/dist/dailymotionBidAdapter.js +1 -1
  57. package/dist/dependencies.json +16 -3
  58. package/dist/discoveryBidAdapter.js +1 -1
  59. package/dist/docereeAdManagerBidAdapter.js +1 -1
  60. package/dist/dspxBidAdapter.js +1 -1
  61. package/dist/dxkultureBidAdapter.js +1 -1
  62. package/dist/eightPodAnalyticsAdapter.js +1 -1
  63. package/dist/eplanningBidAdapter.js +1 -1
  64. package/dist/euidIdSystem.js +1 -1
  65. package/dist/exadsBidAdapter.js +1 -1
  66. package/dist/feedadBidAdapter.js +1 -1
  67. package/dist/finativeBidAdapter.js +1 -1
  68. package/dist/freewheel-sspBidAdapter.js +1 -1
  69. package/dist/gamoshiBidAdapter.js +1 -1
  70. package/dist/gmosspBidAdapter.js +1 -1
  71. package/dist/goldbachBidAdapter.js +1 -1
  72. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  73. package/dist/greenbidsRtdProvider.js +1 -1
  74. package/dist/gridBidAdapter.js +1 -1
  75. package/dist/gumgumBidAdapter.js +1 -1
  76. package/dist/h12mediaBidAdapter.js +1 -1
  77. package/dist/hypelabBidAdapter.js +1 -1
  78. package/dist/id5AnalyticsAdapter.js +1 -1
  79. package/dist/id5IdSystem.js +1 -1
  80. package/dist/idImportLibrary.js +1 -1
  81. package/dist/imdsBidAdapter.js +1 -1
  82. package/dist/improvedigitalBidAdapter.js +1 -1
  83. package/dist/insticatorBidAdapter.js +1 -1
  84. package/dist/intentIqAnalyticsAdapter.js +1 -1
  85. package/dist/ixBidAdapter.js +1 -1
  86. package/dist/jixieBidAdapter.js +1 -1
  87. package/dist/justpremiumBidAdapter.js +1 -1
  88. package/dist/kargoBidAdapter.js +1 -1
  89. package/dist/kimberliteBidAdapter.js +1 -1
  90. package/dist/konduitAnalyticsAdapter.js +1 -1
  91. package/dist/kueezBidAdapter.js +1 -1
  92. package/dist/lassoBidAdapter.js +1 -1
  93. package/dist/lifestreetBidAdapter.js +1 -1
  94. package/dist/liveIntentIdSystem.js +1 -1
  95. package/dist/logicadBidAdapter.js +1 -1
  96. package/dist/loglyliftBidAdapter.js +1 -1
  97. package/dist/luceadBidAdapter.js +1 -1
  98. package/dist/mabidderBidAdapter.js +1 -1
  99. package/dist/magniteAnalyticsAdapter.js +1 -1
  100. package/dist/malltvAnalyticsAdapter.js +1 -1
  101. package/dist/marsmediaBidAdapter.js +1 -1
  102. package/dist/mediafuseBidAdapter.js +1 -1
  103. package/dist/mediagoBidAdapter.js +1 -1
  104. package/dist/medianetAnalyticsAdapter.js +1 -1
  105. package/dist/medianetBidAdapter.js +1 -1
  106. package/dist/mediasquareBidAdapter.js +1 -1
  107. package/dist/mgidBidAdapter.js +1 -1
  108. package/dist/mgidUtils.js +1 -0
  109. package/dist/mgidXBidAdapter.js +1 -1
  110. package/dist/missenaBidAdapter.js +1 -1
  111. package/dist/mobianRtdProvider.js +1 -1
  112. package/dist/navigatorData.js +1 -0
  113. package/dist/nexx360BidAdapter.js +1 -1
  114. package/dist/nobidAnalyticsAdapter.js +1 -1
  115. package/dist/nobidBidAdapter.js +1 -1
  116. package/dist/not-for-prod/prebid.js +176 -170
  117. package/dist/oguryBidAdapter.js +1 -1
  118. package/dist/omsBidAdapter.js +1 -1
  119. package/dist/onetagBidAdapter.js +1 -1
  120. package/dist/ooloAnalyticsAdapter.js +1 -1
  121. package/dist/openxBidAdapter.js +1 -1
  122. package/dist/optidigitalBidAdapter.js +1 -1
  123. package/dist/orbidderBidAdapter.js +1 -1
  124. package/dist/ortbConverter.js +1 -1
  125. package/dist/outbrainBidAdapter.js +1 -1
  126. package/dist/ownadxBidAdapter.js +1 -0
  127. package/dist/pixfutureBidAdapter.js +1 -1
  128. package/dist/prismaBidAdapter.js +1 -1
  129. package/dist/publinkIdSystem.js +1 -1
  130. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  131. package/dist/pubmaticBidAdapter.js +1 -1
  132. package/dist/pubriseBidAdapter.js +1 -0
  133. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  134. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  135. package/dist/pubxaiRtdProvider.js +1 -1
  136. package/dist/pxyzBidAdapter.js +1 -1
  137. package/dist/quantcastBidAdapter.js +1 -1
  138. package/dist/readpeakBidAdapter.js +1 -1
  139. package/dist/redtramBidAdapter.js +1 -1
  140. package/dist/relaidoBidAdapter.js +1 -1
  141. package/dist/retailspotBidAdapter.js +1 -1
  142. package/dist/rhythmoneBidAdapter.js +1 -1
  143. package/dist/riseUtils.js +1 -1
  144. package/dist/rubiconBidAdapter.js +1 -1
  145. package/dist/seedingAllianceBidAdapter.js +1 -1
  146. package/dist/seedtagBidAdapter.js +1 -1
  147. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  148. package/dist/sharethroughBidAdapter.js +1 -1
  149. package/dist/smaatoBidAdapter.js +1 -1
  150. package/dist/smartadserverBidAdapter.js +1 -1
  151. package/dist/smarthubBidAdapter.js +1 -1
  152. package/dist/smartxBidAdapter.js +1 -1
  153. package/dist/smilewantedBidAdapter.js +1 -1
  154. package/dist/snigelBidAdapter.js +1 -1
  155. package/dist/sonobiBidAdapter.js +1 -1
  156. package/dist/sovrnBidAdapter.js +1 -1
  157. package/dist/sspBCBidAdapter.js +1 -1
  158. package/dist/stvBidAdapter.js +1 -1
  159. package/dist/sublimeBidAdapter.js +1 -1
  160. package/dist/taboolaBidAdapter.js +1 -1
  161. package/dist/tappxBidAdapter.js +1 -1
  162. package/dist/targetVideoBidAdapter.js +1 -1
  163. package/dist/teadsBidAdapter.js +1 -1
  164. package/dist/terceptAnalyticsAdapter.js +1 -1
  165. package/dist/themoneytizerBidAdapter.js +1 -1
  166. package/dist/trionBidAdapter.js +1 -1
  167. package/dist/tripleliftBidAdapter.js +1 -1
  168. package/dist/ttdBidAdapter.js +1 -1
  169. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  170. package/dist/uid2IdSystem.js +1 -1
  171. package/dist/underdogmediaBidAdapter.js +1 -1
  172. package/dist/undertoneBidAdapter.js +1 -1
  173. package/dist/unrulyBidAdapter.js +1 -1
  174. package/dist/userId.js +1 -1
  175. package/dist/viantOrtbBidAdapter.js +1 -1
  176. package/dist/vidazooUtils.js +1 -1
  177. package/dist/videobyteBidAdapter.js +1 -1
  178. package/dist/visxBidAdapter.js +1 -1
  179. package/dist/vuukleBidAdapter.js +1 -1
  180. package/dist/widespaceBidAdapter.js +1 -1
  181. package/dist/winrBidAdapter.js +1 -1
  182. package/dist/wurflRtdProvider.js +1 -0
  183. package/dist/yahooAdsBidAdapter.js +1 -1
  184. package/dist/yieldmoBidAdapter.js +1 -1
  185. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  186. package/integrationExamples/gpt/wurflRtdProvider_example.html +106 -0
  187. package/libraries/connectionInfo/connectionUtils.js +33 -0
  188. package/libraries/mgidUtils/mgidUtils.js +73 -0
  189. package/{src/fpd/navigator.js → libraries/navigatorData/navigatorData.js} +1 -1
  190. package/libraries/ortbConverter/processors/video.js +2 -24
  191. package/libraries/riseUtils/index.js +2 -1
  192. package/libraries/vidazooUtils/bidderUtils.js +16 -0
  193. package/modules/.submodules.json +3 -1
  194. package/modules/51DegreesRtdProvider.md +11 -7
  195. package/modules/adagioBidAdapter.js +10 -50
  196. package/modules/admixerBidAdapter.js +12 -5
  197. package/modules/appnexusBidAdapter.js +1 -1
  198. package/modules/ccxBidAdapter.js +6 -2
  199. package/modules/cleanmedianetBidAdapter.js +1 -1
  200. package/modules/contxtfulRtdProvider.js +15 -12
  201. package/modules/cwireBidAdapter.js +13 -9
  202. package/modules/cwireBidAdapter.md +29 -10
  203. package/modules/dailymotionBidAdapter.js +20 -1
  204. package/modules/dailymotionBidAdapter.md +11 -7
  205. package/modules/discoveryBidAdapter.js +1 -1
  206. package/modules/docereeAdManagerBidAdapter.js +9 -12
  207. package/modules/eightPodAnalyticsAdapter.js +13 -1
  208. package/modules/gamoshiBidAdapter.js +1 -1
  209. package/modules/greenbidsAnalyticsAdapter.js +7 -4
  210. package/modules/greenbidsRtdProvider.js +15 -5
  211. package/modules/id5IdSystem.js +14 -93
  212. package/modules/idImportLibrary.js +14 -6
  213. package/modules/ixBidAdapter.js +0 -1
  214. package/modules/mediagoBidAdapter.js +4 -4
  215. package/modules/mgidBidAdapter.js +30 -109
  216. package/modules/mgidXBidAdapter.js +2 -66
  217. package/modules/mobianRtdProvider.js +28 -14
  218. package/modules/omsBidAdapter.js +1 -1
  219. package/modules/ownadxBidAdapter.js +99 -0
  220. package/modules/prismaBidAdapter.js +1 -27
  221. package/modules/pubmaticBidAdapter.js +2 -1
  222. package/modules/pubriseBidAdapter.js +19 -0
  223. package/modules/pubriseBidAdapter.md +79 -0
  224. package/modules/pubxaiAnalyticsAdapter.js +9 -4
  225. package/modules/pubxaiRtdProvider.js +38 -18
  226. package/modules/pubxaiRtdProvider.md +2 -2
  227. package/modules/redtramBidAdapter.js +2 -6
  228. package/modules/rubiconBidAdapter.js +3 -3
  229. package/modules/sharethroughBidAdapter.js +5 -0
  230. package/modules/smarthubBidAdapter.js +2 -0
  231. package/modules/teadsBidAdapter.js +4 -3
  232. package/modules/ttdBidAdapter.js +14 -32
  233. package/modules/undertoneBidAdapter.js +1 -19
  234. package/modules/userId/index.js +1 -1
  235. package/modules/viantOrtbBidAdapter.js +1 -1
  236. package/modules/wurflRtdProvider.js +213 -0
  237. package/modules/wurflRtdProvider.md +67 -0
  238. package/package.json +1 -1
  239. package/src/adloader.js +1 -0
  240. package/src/prebid.js +2 -1
  241. package/src/utils/focusTimeout.js +17 -6
  242. package/src/utils/ttlCollection.js +1 -1
  243. package/src/utils.js +28 -0
  244. package/src/video.js +78 -1
  245. package/test/spec/modules/adagioBidAdapter_spec.js +0 -1
  246. package/test/spec/modules/admixerBidAdapter_spec.js +2 -2
  247. package/test/spec/modules/appnexusBidAdapter_spec.js +0 -33
  248. package/test/spec/modules/ccxBidAdapter_spec.js +81 -0
  249. package/test/spec/modules/cleanmedianetBidAdapter_spec.js +1 -1
  250. package/test/spec/modules/contxtfulRtdProvider_spec.js +19 -19
  251. package/test/spec/modules/dailymotionBidAdapter_spec.js +62 -12
  252. package/test/spec/modules/discoveryBidAdapter_spec.js +1 -1
  253. package/test/spec/modules/docereeAdManagerBidAdapter_spec.js +1 -0
  254. package/test/spec/modules/gamoshiBidAdapter_spec.js +1 -1
  255. package/test/spec/modules/id5IdSystem_spec.js +25 -98
  256. package/test/spec/modules/idImportLibrary_spec.js +14 -0
  257. package/test/spec/modules/ixBidAdapter_spec.js +8 -1
  258. package/test/spec/modules/mobianRtdProvider_spec.js +46 -43
  259. package/test/spec/modules/openxBidAdapter_spec.js +1 -1
  260. package/test/spec/modules/ownadxBidAdapter_spec.js +103 -0
  261. package/test/spec/modules/pubmaticBidAdapter_spec.js +18 -0
  262. package/test/spec/modules/pubriseBidAdapter_spec.js +514 -0
  263. package/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +9 -4
  264. package/test/spec/modules/pubxaiRtdProvider_spec.js +37 -5
  265. package/test/spec/modules/rubiconBidAdapter_spec.js +10 -0
  266. package/test/spec/modules/sharethroughBidAdapter_spec.js +35 -0
  267. package/test/spec/modules/trafficgateBidAdapter_spec.js +1 -1
  268. package/test/spec/modules/ttdBidAdapter_spec.js +10 -1
  269. package/test/spec/modules/viantOrtbBidAdapter_spec.js +1 -1
  270. package/test/spec/modules/vidazooBidAdapter_spec.js +12 -4
  271. package/test/spec/modules/wurflRtdProvider_spec.js +324 -0
  272. package/test/spec/ortbConverter/userId_spec.js +23 -2
  273. package/test/spec/unit/utils/focusTimeout_spec.js +33 -6
  274. package/test/spec/video_spec.js +103 -1
@@ -0,0 +1,213 @@
1
+ import { submodule } from '../src/hook.js';
2
+ import { fetch, sendBeacon } from '../src/ajax.js';
3
+ import { loadExternalScript } from '../src/adloader.js';
4
+ import {
5
+ mergeDeep,
6
+ prefixLog,
7
+ } from '../src/utils.js';
8
+
9
+ // Constants
10
+ const REAL_TIME_MODULE = 'realTimeData';
11
+ const MODULE_NAME = 'wurfl';
12
+
13
+ // WURFL_JS_HOST is the host for the WURFL service endpoints
14
+ const WURFL_JS_HOST = 'https://prebid.wurflcloud.com';
15
+ // WURFL_JS_ENDPOINT_PATH is the path for the WURFL.js endpoint used to load WURFL data
16
+ const WURFL_JS_ENDPOINT_PATH = '/wurfl.js';
17
+ // STATS_ENDPOINT_PATH is the path for the stats endpoint used to send analytics data
18
+ const STATS_ENDPOINT_PATH = '/v1/prebid/stats';
19
+
20
+ const logger = prefixLog('[WURFL RTD Submodule]');
21
+
22
+ // enrichedBidders holds a list of prebid bidder names, of bidders which have been
23
+ // injected with WURFL data
24
+ const enrichedBidders = new Set();
25
+
26
+ /**
27
+ * init initializes the WURFL RTD submodule
28
+ * @param {Object} config Configuration for WURFL RTD submodule
29
+ * @param {Object} userConsent User consent data
30
+ */
31
+ const init = (config, userConsent) => {
32
+ logger.logMessage('initialized');
33
+ return true;
34
+ }
35
+
36
+ /**
37
+ * getBidRequestData enriches the OpenRTB 2.0 device data with WURFL data
38
+ * @param {Object} reqBidsConfigObj Bid request configuration object
39
+ * @param {Function} callback Called on completion
40
+ * @param {Object} config Configuration for WURFL RTD submodule
41
+ * @param {Object} userConsent User consent data
42
+ */
43
+ const getBidRequestData = (reqBidsConfigObj, callback, config, userConsent) => {
44
+ const altHost = config.params?.altHost ?? null;
45
+ const isDebug = config.params?.debug ?? false;
46
+
47
+ const bidders = new Set();
48
+ reqBidsConfigObj.adUnits.forEach(adUnit => {
49
+ adUnit.bids.forEach(bid => {
50
+ bidders.add(bid.bidder);
51
+ });
52
+ });
53
+
54
+ let host = WURFL_JS_HOST;
55
+ if (altHost) {
56
+ host = altHost;
57
+ }
58
+
59
+ const url = new URL(host);
60
+ url.pathname = WURFL_JS_ENDPOINT_PATH;
61
+
62
+ if (isDebug) {
63
+ url.searchParams.set('debug', 'true')
64
+ }
65
+
66
+ url.searchParams.set('mode', 'prebid')
67
+ logger.logMessage('url', url.toString());
68
+
69
+ try {
70
+ loadExternalScript(url.toString(), MODULE_NAME, () => {
71
+ logger.logMessage('script injected');
72
+ window.WURFLPromises.complete.then((res) => {
73
+ logger.logMessage('received data', res);
74
+ if (!res.wurfl_pbjs) {
75
+ logger.logError('invalid WURFL.js for Prebid response');
76
+ } else {
77
+ enrichBidderRequests(reqBidsConfigObj, bidders, res);
78
+ }
79
+ callback();
80
+ });
81
+ });
82
+ } catch (err) {
83
+ logger.logError(err);
84
+ callback();
85
+ }
86
+ }
87
+
88
+ /**
89
+ * enrichBidderRequests enriches the OpenRTB 2.0 device data with WURFL data for Business Edition
90
+ * @param {Object} reqBidsConfigObj Bid request configuration object
91
+ * @param {Array} bidders List of bidders
92
+ * @param {Object} wjsResponse WURFL.js response
93
+ */
94
+ function enrichBidderRequests(reqBidsConfigObj, bidders, wjsResponse) {
95
+ const authBidders = wjsResponse.wurfl_pbjs?.authorized_bidders ?? {};
96
+ const caps = wjsResponse.wurfl_pbjs?.caps ?? [];
97
+
98
+ bidders.forEach((bidderCode) => {
99
+ if (bidderCode in authBidders) {
100
+ // inject WURFL data
101
+ enrichedBidders.add(bidderCode);
102
+ const data = bidderData(wjsResponse.WURFL, caps, authBidders[bidderCode]);
103
+ logger.logMessage(`injecting data for ${bidderCode}: `, data);
104
+ enrichBidderRequest(reqBidsConfigObj, bidderCode, data);
105
+ return;
106
+ }
107
+ // inject WURFL low entropy data
108
+ const data = lowEntropyData(wjsResponse.WURFL, wjsResponse.wurfl_pbjs?.low_entropy_caps);
109
+ logger.logMessage(`injecting low entropy data for ${bidderCode}: `, data);
110
+ enrichBidderRequest(reqBidsConfigObj, bidderCode, data);
111
+ });
112
+ }
113
+
114
+ /**
115
+ * bidderData returns the WURFL data for a bidder
116
+ * @param {Object} wurflData WURFL data
117
+ * @param {Array} caps Capability list
118
+ * @param {Array} filter Filter list
119
+ * @returns {Object} Bidder data
120
+ */
121
+ export const bidderData = (wurflData, caps, filter) => {
122
+ const data = {};
123
+ caps.forEach((cap, index) => {
124
+ if (!filter.includes(index)) {
125
+ return;
126
+ }
127
+ if (cap in wurflData) {
128
+ data[cap] = wurflData[cap];
129
+ }
130
+ });
131
+ return data;
132
+ }
133
+
134
+ /**
135
+ * lowEntropyData returns the WURFL low entropy data
136
+ * @param {Object} wurflData WURFL data
137
+ * @param {Array} lowEntropyCaps Low entropy capability list
138
+ * @returns {Object} Bidder data
139
+ */
140
+ export const lowEntropyData = (wurflData, lowEntropyCaps) => {
141
+ const data = {};
142
+ lowEntropyCaps.forEach((cap, _) => {
143
+ let value = wurflData[cap];
144
+ if (cap == 'complete_device_name') {
145
+ value = value.replace(/Apple (iP(hone|ad|od)).*/, 'Apple iP$2');
146
+ }
147
+ data[cap] = value;
148
+ });
149
+ return data;
150
+ }
151
+
152
+ /**
153
+ * enrichBidderRequest enriches the bidder request with WURFL data
154
+ * @param {Object} reqBidsConfigObj Bid request configuration object
155
+ * @param {String} bidderCode Bidder code
156
+ * @param {Object} wurflData WURFL data
157
+ */
158
+ export const enrichBidderRequest = (reqBidsConfigObj, bidderCode, wurflData) => {
159
+ const ortb2data = {
160
+ 'device': {
161
+ 'ext': {
162
+ 'wurfl': wurflData,
163
+ }
164
+ },
165
+ };
166
+ mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, { [bidderCode]: ortb2data });
167
+ }
168
+
169
+ /**
170
+ * onAuctionEndEvent is called when the auction ends
171
+ * @param {Object} auctionDetails Auction details
172
+ * @param {Object} config Configuration for WURFL RTD submodule
173
+ * @param {Object} userConsent User consent data
174
+ */
175
+ function onAuctionEndEvent(auctionDetails, config, userConsent) {
176
+ const altHost = config.params?.altHost ?? null;
177
+
178
+ let host = WURFL_JS_HOST;
179
+ if (altHost) {
180
+ host = altHost;
181
+ }
182
+
183
+ const url = new URL(host);
184
+ url.pathname = STATS_ENDPOINT_PATH;
185
+
186
+ if (enrichedBidders.size === 0) {
187
+ return;
188
+ }
189
+
190
+ var payload = JSON.stringify({ bidders: [...enrichedBidders] });
191
+ const sentBeacon = sendBeacon(url.toString(), payload);
192
+ if (sentBeacon) {
193
+ return;
194
+ }
195
+
196
+ fetch(url.toString(), {
197
+ method: 'POST',
198
+ body: payload,
199
+ mode: 'no-cors',
200
+ keepalive: true
201
+ });
202
+ }
203
+
204
+ // The WURFL submodule
205
+ export const wurflSubmodule = {
206
+ name: MODULE_NAME,
207
+ init,
208
+ getBidRequestData,
209
+ onAuctionEndEvent,
210
+ }
211
+
212
+ // Register the WURFL submodule as submodule of realTimeData
213
+ submodule(REAL_TIME_MODULE, wurflSubmodule);
@@ -0,0 +1,67 @@
1
+ # WURFL Real-time Data Submodule
2
+
3
+ ## Overview
4
+
5
+ Module Name: WURFL Rtd Provider
6
+ Module Type: Rtd Provider
7
+ Maintainer: prebid@scientiamobile.com
8
+
9
+ ## Description
10
+
11
+ The WURFL RTD module enriches the OpenRTB 2.0 device data with [WURFL data](https://www.scientiamobile.com/wurfl-js-business-edition-at-the-intersection-of-javascript-and-enterprise/).
12
+ The module sets the WURFL data in `device.ext.wurfl` and all the bidder adapters will always receive the low entry capabilites like `is_mobile`, `complete_device_name` and `form_factor`.
13
+
14
+ For a more detailed analysis bidders can subscribe to detect iPhone and iPad models and receive additional [WURFL device capabilities](https://www.scientiamobile.com/capabilities/?products%5B%5D=wurfl-js).
15
+
16
+ ## User-Agent Client Hints
17
+
18
+ WURFL.js is fully compatible with Chromium's User-Agent Client Hints (UA-CH) initiative. If User-Agent Client Hints are absent in the HTTP headers that WURFL.js receives, the service will automatically fall back to using the User-Agent Client Hints' JS API to fetch [high entropy client hint values](https://wicg.github.io/ua-client-hints/#getHighEntropyValues) from the client device. However, we recommend that you explicitly opt-in/advertise support for User-Agent Client Hints on your website and delegate them to the WURFL.js service for the fastest detection experience. Our documentation regarding implementing User-Agent Client Hint support [is available here](https://docs.scientiamobile.com/guides/implementing-useragent-clienthints).
19
+
20
+ ## Usage
21
+
22
+ ### Build
23
+ ```
24
+ gulp build --modules="wurflRtdProvider,appnexusBidAdapter,..."
25
+ ```
26
+
27
+ ### Configuration
28
+
29
+ Use `setConfig` to instruct Prebid.js to initilize the WURFL RTD module, as specified below.
30
+
31
+ This module is configured as part of the `realTimeData.dataProviders`
32
+
33
+ ```javascript
34
+ var TIMEOUT = 1000;
35
+ pbjs.setConfig({
36
+ realTimeData: {
37
+ auctionDelay: TIMEOUT,
38
+ dataProviders: [{
39
+ name: 'wurfl',
40
+ waitForIt: true,
41
+ params: {
42
+ debug: false
43
+ }
44
+ }]
45
+ }
46
+ });
47
+ ```
48
+
49
+ ### Parameters
50
+
51
+ | Name | Type | Description | Default |
52
+ | :------------------------ | :------------ | :--------------------------------------------------------------- |:----------------- |
53
+ | name | String | Real time data module name | Always 'wurfl' |
54
+ | waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (optional) | `false` |
55
+ | params | Object | | |
56
+ | params.altHost | String | Alternate host to connect to WURFL.js | |
57
+ | params.debug | Boolean | Enable debug | `false` |
58
+
59
+ ## Testing
60
+
61
+ To view an example of how the WURFL RTD module works :
62
+
63
+ `gulp serve --modules=wurflRtdProvider,appnexusBidAdapter`
64
+
65
+ and then point your browser at:
66
+
67
+ `http://localhost:9999/integrationExamples/gpt/wurflRtdProvider_example.html`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prebid.js",
3
- "version": "9.6.0",
3
+ "version": "9.8.0",
4
4
  "description": "Header Bidding Management Library",
5
5
  "main": "src/prebid.public.js",
6
6
  "exports": {
package/src/adloader.js CHANGED
@@ -32,6 +32,7 @@ const _approvedLoadExternalJSList = [
32
32
  'dynamicAdBoost',
33
33
  '51Degrees',
34
34
  'symitridap',
35
+ 'wurfl',
35
36
  // UserId Submodules
36
37
  'justtag',
37
38
  'tncId',
package/src/prebid.js CHANGED
@@ -41,7 +41,7 @@ import {enrichFPD} from './fpd/enrichment.js';
41
41
  import {allConsent} from './consentHandler.js';
42
42
  import {insertLocatorFrame, renderAdDirect} from './adRendering.js';
43
43
  import {getHighestCpm} from './utils/reducers.js';
44
- import {fillVideoDefaults} from './video.js';
44
+ import {fillVideoDefaults, validateOrtbVideoFields} from './video.js';
45
45
 
46
46
  const pbjsInstance = getGlobal();
47
47
  const { triggerUserSyncs } = userSync;
@@ -134,6 +134,7 @@ function validateVideoMediaType(adUnit) {
134
134
  delete validatedAdUnit.mediaTypes.video.playerSize;
135
135
  }
136
136
  }
137
+ validateOrtbVideoFields(validatedAdUnit);
137
138
  return validatedAdUnit;
138
139
  }
139
140
 
@@ -1,16 +1,27 @@
1
- let outOfFocusStart;
1
+ let outOfFocusStart = null; // enforce null otherwise it could be undefined and the callback wouldn't execute
2
2
  let timeOutOfFocus = 0;
3
3
  let suspendedTimeouts = [];
4
4
 
5
- document.addEventListener('visibilitychange', () => {
5
+ function trackTimeOutOfFocus() {
6
6
  if (document.hidden) {
7
7
  outOfFocusStart = Date.now()
8
8
  } else {
9
- timeOutOfFocus += Date.now() - outOfFocusStart
10
- suspendedTimeouts.forEach(({ callback, startTime, setTimerId }) => setTimerId(setFocusTimeout(callback, timeOutOfFocus - startTime)()))
9
+ timeOutOfFocus += Date.now() - (outOfFocusStart ?? 0); // when the page is loaded in hidden state outOfFocusStart is undefined, which results in timeoutOffset being NaN
11
10
  outOfFocusStart = null;
11
+ suspendedTimeouts.forEach(({ callback, startTime, setTimerId }) => setTimerId(setFocusTimeout(callback, timeOutOfFocus - startTime)()));
12
+ suspendedTimeouts = [];
12
13
  }
13
- });
14
+ }
15
+
16
+ document.addEventListener('visibilitychange', trackTimeOutOfFocus);
17
+
18
+ export function reset() {
19
+ outOfFocusStart = null;
20
+ timeOutOfFocus = 0;
21
+ suspendedTimeouts = [];
22
+ document.removeEventListener('visibilitychange', trackTimeOutOfFocus);
23
+ document.addEventListener('visibilitychange', trackTimeOutOfFocus);
24
+ }
14
25
 
15
26
  /**
16
27
  * Wraps native setTimeout function in order to count time only when page is focused
@@ -19,7 +30,7 @@ document.addEventListener('visibilitychange', () => {
19
30
  * @param {number} [milliseconds] - Minimum duration (in milliseconds) that the callback will be executed after
20
31
  * @returns {function(*): (number)} - Getter function for current timer id
21
32
  */
22
- export default function setFocusTimeout(callback, milliseconds) {
33
+ export function setFocusTimeout(callback, milliseconds) {
23
34
  const startTime = timeOutOfFocus;
24
35
  let timerId = setTimeout(() => {
25
36
  if (timeOutOfFocus === startTime && outOfFocusStart == null) {
@@ -1,6 +1,6 @@
1
1
  import {GreedyPromise} from './promise.js';
2
2
  import {binarySearch, logError, timestamp} from '../utils.js';
3
- import setFocusTimeout from './focusTimeout.js';
3
+ import {setFocusTimeout} from './focusTimeout.js';
4
4
 
5
5
  /**
6
6
  * Create a set-like collection that automatically forgets items after a certain time.
package/src/utils.js CHANGED
@@ -1258,3 +1258,31 @@ export function setOnAny(collection, key) {
1258
1258
  }
1259
1259
  return undefined;
1260
1260
  }
1261
+
1262
+ export function extractDomainFromHost(pageHost) {
1263
+ let domain = null;
1264
+ try {
1265
+ let domains = /[-\w]+\.([-\w]+|[-\w]{3,}|[-\w]{1,3}\.[-\w]{2})$/i.exec(pageHost);
1266
+ if (domains != null && domains.length > 0) {
1267
+ domain = domains[0];
1268
+ for (let i = 1; i < domains.length; i++) {
1269
+ if (domains[i].length > domain.length) {
1270
+ domain = domains[i];
1271
+ }
1272
+ }
1273
+ }
1274
+ } catch (e) {
1275
+ domain = null;
1276
+ }
1277
+ return domain;
1278
+ }
1279
+
1280
+ export function triggerNurlWithCpm(bid, cpm) {
1281
+ if (isStr(bid.nurl) && bid.nurl !== '') {
1282
+ bid.nurl = bid.nurl.replace(
1283
+ /\${AUCTION_PRICE}/,
1284
+ cpm
1285
+ );
1286
+ triggerPixel(bid.nurl);
1287
+ }
1288
+ }
package/src/video.js CHANGED
@@ -1,4 +1,4 @@
1
- import {deepAccess, logError} from './utils.js';
1
+ import {deepAccess, isArrayOfNums, isInteger, isNumber, isPlainObject, isStr, logError, logWarn} from './utils.js';
2
2
  import {config} from '../src/config.js';
3
3
  import {hook} from './hook.js';
4
4
  import {auctionManager} from './auctionManager.js';
@@ -6,6 +6,47 @@ import {auctionManager} from './auctionManager.js';
6
6
  export const OUTSTREAM = 'outstream';
7
7
  export const INSTREAM = 'instream';
8
8
 
9
+ /**
10
+ * List of OpenRTB 2.x video object properties with simple validators.
11
+ * Not included: `companionad`, `durfloors`, `ext`
12
+ * reference: https://github.com/InteractiveAdvertisingBureau/openrtb2.x/blob/main/2.6.md
13
+ */
14
+ export const ORTB_VIDEO_PARAMS = new Map([
15
+ [ 'mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string') ],
16
+ [ 'minduration', isInteger ],
17
+ [ 'maxduration', isInteger ],
18
+ [ 'startdelay', isInteger ],
19
+ [ 'maxseq', isInteger ],
20
+ [ 'poddur', isInteger ],
21
+ [ 'protocols', isArrayOfNums ],
22
+ [ 'w', isInteger ],
23
+ [ 'h', isInteger ],
24
+ [ 'podid', isStr ],
25
+ [ 'podseq', isInteger ],
26
+ [ 'rqddurs', isArrayOfNums ],
27
+ [ 'placement', isInteger ], // deprecated, see plcmt
28
+ [ 'plcmt', isInteger ],
29
+ [ 'linearity', isInteger ],
30
+ [ 'skip', value => [1, 0].includes(value) ],
31
+ [ 'skipmin', isInteger ],
32
+ [ 'skipafter', isInteger ],
33
+ [ 'sequence', isInteger ], // deprecated
34
+ [ 'slotinpod', isInteger ],
35
+ [ 'mincpmpersec', isNumber ],
36
+ [ 'battr', isArrayOfNums ],
37
+ [ 'maxextended', isInteger ],
38
+ [ 'minbitrate', isInteger ],
39
+ [ 'maxbitrate', isInteger ],
40
+ [ 'boxingallowed', isInteger ],
41
+ [ 'playbackmethod', isArrayOfNums ],
42
+ [ 'playbackend', isInteger ],
43
+ [ 'delivery', isArrayOfNums ],
44
+ [ 'pos', isInteger ],
45
+ [ 'api', isArrayOfNums ],
46
+ [ 'companiontype', isArrayOfNums ],
47
+ [ 'poddedupe', isArrayOfNums ]
48
+ ]);
49
+
9
50
  export function fillVideoDefaults(adUnit) {
10
51
  const video = adUnit?.mediaTypes?.video;
11
52
  if (video != null && video.plcmt == null) {
@@ -17,6 +58,42 @@ export function fillVideoDefaults(adUnit) {
17
58
  }
18
59
  }
19
60
 
61
+ /**
62
+ * validateOrtbVideoFields mutates the `adUnit.mediaTypes.video` object by removing invalid ortb properties (default).
63
+ * The onInvalidParam callback can be used to handle invalid properties differently.
64
+ * Other properties are ignored and kept as is.
65
+ *
66
+ * @param {Object} adUnit - The adUnit object.
67
+ * @param {Function} onInvalidParam - The callback function to be called with key, value, and adUnit.
68
+ * @returns {void}
69
+ */
70
+ export function validateOrtbVideoFields(adUnit, onInvalidParam) {
71
+ const videoParams = adUnit?.mediaTypes?.video;
72
+
73
+ if (!isPlainObject(videoParams)) {
74
+ logWarn(`validateOrtbVideoFields: videoParams must be an object.`);
75
+ return;
76
+ }
77
+
78
+ if (videoParams != null) {
79
+ Object.entries(videoParams)
80
+ .forEach(([key, value]) => {
81
+ if (!ORTB_VIDEO_PARAMS.has(key)) {
82
+ return
83
+ }
84
+ const isValid = ORTB_VIDEO_PARAMS.get(key)(value);
85
+ if (!isValid) {
86
+ if (typeof onInvalidParam === 'function') {
87
+ onInvalidParam(key, value, adUnit);
88
+ } else {
89
+ delete videoParams[key];
90
+ logWarn(`Invalid prop in adUnit "${adUnit.code}": Invalid value for mediaTypes.video.${key} ORTB property. The property has been removed.`);
91
+ }
92
+ }
93
+ });
94
+ }
95
+ }
96
+
20
97
  /**
21
98
  * @typedef {object} VideoBid
22
99
  * @property {string} adId id of the bid
@@ -603,7 +603,6 @@ describe('Adagio bid adapter', () => {
603
603
  const requests = spec.buildRequests([bid01], bidderRequest);
604
604
  expect(requests).to.have.lengthOf(1);
605
605
  expect(requests[0].data.adUnits[0].mediaTypes.video).to.deep.equal(expected);
606
- sinon.assert.calledTwice(utils.logWarn.withArgs(sinon.match(new RegExp(/^Adagio: The OpenRTB/))));
607
606
  });
608
607
  });
609
608
 
@@ -226,12 +226,12 @@ describe('AdmixerAdapter', function () {
226
226
  },
227
227
  };
228
228
  it('gets floor', function () {
229
- bidderRequest.getFloor = () => {
229
+ validRequest[0].getFloor = () => {
230
230
  return { floor: 0.6 };
231
231
  };
232
232
  const request = spec.buildRequests(validRequest, bidderRequest);
233
233
  const payload = request.data;
234
- expect(payload.bidFloor).to.deep.equal(0.6);
234
+ expect(payload.imps[0].bidFloor).to.deep.equal(0.6);
235
235
  });
236
236
  });
237
237
 
@@ -1783,7 +1783,6 @@ describe('AppNexusAdapter', function () {
1783
1783
  'cpm': 0.5,
1784
1784
  'cpm_publisher_currency': 0.5,
1785
1785
  'publisher_currency_code': '$',
1786
- 'publisher_currency_codename': 'USD',
1787
1786
  'client_initiated_ad_counting': true,
1788
1787
  'viewability': {
1789
1788
  'config': '<script type=\'text/javascript\' async=\'true\' src=\'https://cdn.adnxs.com/v/s/152/trk.js#v;vk=appnexus.com-omid;tv=native1-18h;dom_id=%native_dom_id%;st=0;d=1x1;vc=iab;vid_ccr=1;tag_id=13232354;cb=https%3A%2F%2Fams1-ib.adnxs.com%2Fvevent%3Freferrer%3Dhttps253A%252F%252Ftestpages-pmahe.tp.adnxs.net%252F01_basic_single%26e%3DwqT_3QLNB6DNAwAAAwDWAAUBCLfl_-MFEMStk8u3lPTjRxih88aF0fq_2QsqNgkAAAECCCRAEQEHEAAAJEAZEQkAIREJACkRCQAxEQmoMOLRpwY47UhA7UhIAlCDy74uWJzxW2AAaM26dXjzjwWAAQGKAQNVU0SSAQEG8FCYAQGgAQGoAQGwAQC4AQHAAQTIAQLQAQDYAQDgAQDwAQCKAjt1ZignYScsIDI1Mjk4ODUsIDE1NTE4ODkwNzkpO3VmKCdyJywgOTc0OTQ0MDM2HgDwjZIC8QEha0RXaXBnajgtTHdLRUlQTHZpNFlBQ0NjOFZzd0FEZ0FRQVJJN1VoUTR0R25CbGdBWU1rR2FBQndMSGlrTDRBQlVvZ0JwQy1RQVFHWUFRR2dBUUdvQVFPd0FRQzVBZk90YXFRQUFDUkF3UUh6cldxa0FBQWtRTWtCbWo4dDA1ZU84VF9aQVFBQUEBAyRQQV80QUVBOVFFAQ4sQW1BSUFvQUlBdFFJBRAAdg0IeHdBSUF5QUlBNEFJQTZBSUEtQUlBZ0FNQm1BTUJxQVAFzIh1Z01KUVUxVE1UbzBNekl3NEFPVENBLi6aAmEhUXcxdGNRagUoEfQkblBGYklBUW9BRAl8AEEBqAREbzJEABRRSk1JU1EBGwRBQQGsAFURDAxBQUFXHQzwWNgCAOACrZhI6gIzaHR0cDovL3Rlc3RwYWdlcy1wbWFoZS50cC5hZG54cy5uZXQvMDFfYmFzaWNfc2luZ2xl8gITCg9DVVNUT01fTU9ERUxfSUQSAPICGgoWMhYAPExFQUZfTkFNRRIA8gIeCho2HQAIQVNUAT7wnElGSUVEEgCAAwCIAwGQAwCYAxegAwGqAwDAA-CoAcgDANgD8ao-4AMA6AMA-AMBgAQAkgQNL3V0L3YzL3ByZWJpZJgEAKIECjEwLjIuMTIuMzioBIqpB7IEDggAEAEYACAAKAAwADgCuAQAwAQAyAQA0gQOOTMyNSNBTVMxOjQzMjDaBAIIAeAEAfAEg8u-LogFAZgFAKAF______8BAxgBwAUAyQUABQEU8D_SBQkJBQt8AAAA2AUB4AUB8AWZ9CH6BQQIABAAkAYBmAYAuAYAwQYBITAAAPA_yAYA2gYWChAAOgEAGBAAGADgBgw.%26s%3D971dce9d49b6bee447c8a58774fb30b40fe98171;ts=1551889079;cet=0;cecb=\'></script>'
@@ -1868,38 +1867,6 @@ describe('AppNexusAdapter', function () {
1868
1867
  expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0]));
1869
1868
  });
1870
1869
 
1871
- it('should parse non-default currency', function () {
1872
- let eurCpmResponse = deepClone(response);
1873
- eurCpmResponse.tags[0].ads[0].publisher_currency_codename = 'EUR';
1874
-
1875
- let bidderRequest = {
1876
- bidderCode: 'appnexus',
1877
- bids: [{
1878
- bidId: '3db3773286ee59',
1879
- adUnitCode: 'code'
1880
- }]
1881
- };
1882
-
1883
- let result = spec.interpretResponse({ body: eurCpmResponse }, { bidderRequest });
1884
- expect(result[0].currency).to.equal('EUR');
1885
- });
1886
-
1887
- it('should parse default currency', function () {
1888
- let defaultCpmResponse = deepClone(response);
1889
- delete defaultCpmResponse.tags[0].ads[0].publisher_currency_codename;
1890
-
1891
- let bidderRequest = {
1892
- bidderCode: 'appnexus',
1893
- bids: [{
1894
- bidId: '3db3773286ee59',
1895
- adUnitCode: 'code'
1896
- }]
1897
- };
1898
-
1899
- let result = spec.interpretResponse({ body: defaultCpmResponse }, { bidderRequest });
1900
- expect(result[0].currency).to.equal('USD');
1901
- });
1902
-
1903
1870
  it('should reject 0 cpm bids', function () {
1904
1871
  let zeroCpmResponse = deepClone(response);
1905
1872
  zeroCpmResponse.tags[0].ads[0].cpm = 0;