prebid.js 7.14.0 → 7.16.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 (239) hide show
  1. package/.circleci/config.yml +3 -0
  2. package/dist/1plusXRtdProvider.js +1 -1
  3. package/dist/33acrossBidAdapter.js +1 -1
  4. package/dist/adagioBidAdapter.js +1 -1
  5. package/dist/adbookpspBidAdapter.js +1 -1
  6. package/dist/adfBidAdapter.js +1 -1
  7. package/dist/adgenerationBidAdapter.js +1 -1
  8. package/dist/adkernelAdnBidAdapter.js +1 -1
  9. package/dist/adkernelBidAdapter.js +1 -1
  10. package/dist/adlooxAdServerVideo.js +1 -1
  11. package/dist/adlooxRtdProvider.js +1 -1
  12. package/dist/adrelevantisBidAdapter.js +1 -1
  13. package/dist/adtrgtmeBidAdapter.js +1 -1
  14. package/dist/adtrueBidAdapter.js +1 -1
  15. package/dist/adxcgBidAdapter.js +1 -1
  16. package/dist/airgridRtdProvider.js +1 -1
  17. package/dist/ajaBidAdapter.js +1 -1
  18. package/dist/amxBidAdapter.js +1 -1
  19. package/dist/amxIdSystem.js +1 -1
  20. package/dist/aolBidAdapter.js +1 -1
  21. package/dist/appierAnalyticsAdapter.js +1 -1
  22. package/dist/appnexusBidAdapter.js +1 -1
  23. package/dist/asoBidAdapter.js +1 -1
  24. package/dist/axonixBidAdapter.js +1 -1
  25. package/dist/beachfrontBidAdapter.js +1 -1
  26. package/dist/bidglassBidAdapter.js +1 -1
  27. package/dist/big-richmediaBidAdapter.js +1 -1
  28. package/dist/bizzclickBidAdapter.js +1 -1
  29. package/dist/bliinkBidAdapter.js +1 -1
  30. package/dist/bluebillywigBidAdapter.js +1 -1
  31. package/dist/brandmetricsRtdProvider.js +1 -1
  32. package/dist/bridgewellBidAdapter.js +1 -1
  33. package/dist/brightMountainMediaBidAdapter.js +1 -1
  34. package/dist/brightcomBidAdapter.js +1 -1
  35. package/dist/browsiRtdProvider.js +1 -1
  36. package/dist/categoryTranslation.js +1 -1
  37. package/dist/concertBidAdapter.js +1 -1
  38. package/dist/connectadBidAdapter.js +1 -1
  39. package/dist/consentManagement.js +1 -1
  40. package/dist/consentManagementUsp.js +1 -1
  41. package/dist/consumableBidAdapter.js +1 -1
  42. package/dist/conversantBidAdapter.js +1 -1
  43. package/dist/cpmstarBidAdapter.js +1 -1
  44. package/dist/craftBidAdapter.js +1 -1
  45. package/dist/criteoBidAdapter.js +1 -1
  46. package/dist/currency.js +1 -1
  47. package/dist/dataControllerModule.js +1 -1
  48. package/dist/dchain.js +1 -1
  49. package/dist/deepintentBidAdapter.js +1 -1
  50. package/dist/dependencies.json +3 -0
  51. package/dist/dgkeywordRtdProvider.js +1 -1
  52. package/dist/dianomiBidAdapter.js +1 -1
  53. package/dist/distroscaleBidAdapter.js +1 -1
  54. package/dist/dspxBidAdapter.js +1 -1
  55. package/dist/eplanningBidAdapter.js +1 -1
  56. package/dist/etargetBidAdapter.js +1 -1
  57. package/dist/feedadBidAdapter.js +1 -1
  58. package/dist/finativeBidAdapter.js +1 -1
  59. package/dist/fpdModule.js +1 -1
  60. package/dist/gamoshiBidAdapter.js +1 -1
  61. package/dist/glimpseBidAdapter.js +1 -1
  62. package/dist/gmosspBidAdapter.js +1 -1
  63. package/dist/goldbachBidAdapter.js +1 -1
  64. package/dist/gothamadsBidAdapter.js +1 -1
  65. package/dist/gridBidAdapter.js +1 -1
  66. package/dist/gridNMBidAdapter.js +1 -1
  67. package/dist/gumgumBidAdapter.js +1 -1
  68. package/dist/h12mediaBidAdapter.js +1 -1
  69. package/dist/iasRtdProvider.js +1 -1
  70. package/dist/id5IdSystem.js +1 -1
  71. package/dist/imRtdProvider.js +1 -1
  72. package/dist/impactifyBidAdapter.js +1 -1
  73. package/dist/improvedigitalBidAdapter.js +1 -1
  74. package/dist/inmarBidAdapter.js +1 -1
  75. package/dist/insticatorBidAdapter.js +1 -1
  76. package/dist/ixBidAdapter.js +1 -1
  77. package/dist/justpremiumBidAdapter.js +1 -1
  78. package/dist/konduitAnalyticsAdapter.js +1 -1
  79. package/dist/kueezBidAdapter.js +1 -1
  80. package/dist/lassoBidAdapter.js +1 -1
  81. package/dist/lifestreetBidAdapter.js +1 -1
  82. package/dist/liveIntentAnalyticsAdapter.js +1 -0
  83. package/dist/liveyieldAnalyticsAdapter.js +1 -1
  84. package/dist/logicadBidAdapter.js +1 -1
  85. package/dist/loglyliftBidAdapter.js +1 -1
  86. package/dist/luponmediaBidAdapter.js +1 -1
  87. package/dist/malltvAnalyticsAdapter.js +1 -1
  88. package/dist/marsmediaBidAdapter.js +1 -1
  89. package/dist/mass.js +1 -1
  90. package/dist/mediafuseBidAdapter.js +1 -1
  91. package/dist/mediakeysBidAdapter.js +1 -1
  92. package/dist/mediasniperBidAdapter.js +1 -1
  93. package/dist/mediasquareBidAdapter.js +1 -1
  94. package/dist/mgidBidAdapter.js +1 -1
  95. package/dist/minutemediaBidAdapter.js +1 -1
  96. package/dist/multibid.js +1 -1
  97. package/dist/naveggIdSystem.js +1 -1
  98. package/dist/newspassidBidAdapter.js +1 -1
  99. package/dist/nextMillenniumBidAdapter.js +1 -1
  100. package/dist/not-for-prod/prebid.js +172 -171
  101. package/dist/oguryBidAdapter.js +1 -1
  102. package/dist/oneKeyRtdProvider.js +1 -1
  103. package/dist/onetagBidAdapter.js +1 -1
  104. package/dist/ooloAnalyticsAdapter.js +1 -1
  105. package/dist/openxBidAdapter.js +1 -1
  106. package/dist/openxOrtbBidAdapter.js +1 -1
  107. package/dist/operaadsBidAdapter.js +1 -1
  108. package/dist/outbrainBidAdapter.js +1 -1
  109. package/dist/ozoneBidAdapter.js +1 -1
  110. package/dist/parrableIdSystem.js +1 -1
  111. package/dist/permutiveRtdProvider.js +1 -1
  112. package/dist/pixfutureBidAdapter.js +1 -1
  113. package/dist/prebid-core.js +2 -2
  114. package/dist/prebidServerBidAdapter.js +1 -1
  115. package/dist/priceFloors.js +1 -1
  116. package/dist/pubCommonId.js +1 -1
  117. package/dist/pubgeniusBidAdapter.js +1 -1
  118. package/dist/publinkIdSystem.js +1 -1
  119. package/dist/pubmaticBidAdapter.js +1 -1
  120. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  121. package/dist/pubwiseBidAdapter.js +1 -1
  122. package/dist/pubxBidAdapter.js +1 -1
  123. package/dist/pxyzBidAdapter.js +1 -1
  124. package/dist/quantcastBidAdapter.js +1 -1
  125. package/dist/readpeakBidAdapter.js +1 -1
  126. package/dist/relaidoBidAdapter.js +1 -1
  127. package/dist/rhythmoneBidAdapter.js +1 -1
  128. package/dist/riseBidAdapter.js +1 -1
  129. package/dist/rtdModule.js +1 -1
  130. package/dist/rubiconAnalyticsAdapter.js +1 -1
  131. package/dist/rubiconBidAdapter.js +1 -1
  132. package/dist/seedingAllianceBidAdapter.js +1 -1
  133. package/dist/seedtagBidAdapter.js +1 -1
  134. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  135. package/dist/sharethroughBidAdapter.js +1 -1
  136. package/dist/shinezBidAdapter.js +1 -1
  137. package/dist/sirdataRtdProvider.js +1 -1
  138. package/dist/smaatoBidAdapter.js +1 -1
  139. package/dist/smartadserverBidAdapter.js +1 -1
  140. package/dist/smartxBidAdapter.js +1 -1
  141. package/dist/smilewantedBidAdapter.js +1 -1
  142. package/dist/sonobiBidAdapter.js +1 -1
  143. package/dist/sovrnAnalyticsAdapter.js +1 -1
  144. package/dist/sovrnBidAdapter.js +1 -1
  145. package/dist/spotxBidAdapter.js +1 -1
  146. package/dist/sspBCBidAdapter.js +1 -1
  147. package/dist/stroeerCoreBidAdapter.js +1 -1
  148. package/dist/sublimeBidAdapter.js +1 -1
  149. package/dist/synacormediaBidAdapter.js +1 -1
  150. package/dist/targetVideoBidAdapter.js +1 -1
  151. package/dist/teadsBidAdapter.js +1 -1
  152. package/dist/trionBidAdapter.js +1 -1
  153. package/dist/tripleliftBidAdapter.js +1 -1
  154. package/dist/ttdBidAdapter.js +1 -1
  155. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  156. package/dist/ucfunnelBidAdapter.js +1 -1
  157. package/dist/underdogmediaBidAdapter.js +1 -1
  158. package/dist/undertoneBidAdapter.js +1 -1
  159. package/dist/userId.js +1 -1
  160. package/dist/vidazooBidAdapter.js +1 -1
  161. package/dist/videobyteBidAdapter.js +1 -1
  162. package/dist/viewability.js +1 -1
  163. package/dist/visxBidAdapter.js +1 -1
  164. package/dist/vuukleBidAdapter.js +1 -1
  165. package/dist/weboramaRtdProvider.js +1 -1
  166. package/dist/widespaceBidAdapter.js +1 -1
  167. package/dist/winrBidAdapter.js +1 -1
  168. package/dist/yahoosspBidAdapter.js +1 -1
  169. package/dist/yieldliftBidAdapter.js +1 -1
  170. package/dist/yieldmoBidAdapter.js +1 -1
  171. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  172. package/dist/zeta_global_sspBidAdapter.js +1 -1
  173. package/modules/amxBidAdapter.js +98 -78
  174. package/modules/appnexusBidAdapter.js +73 -12
  175. package/modules/categoryTranslation.js +9 -8
  176. package/modules/consentManagement.js +4 -2
  177. package/modules/consentManagementUsp.js +17 -14
  178. package/modules/cpmstarBidAdapter.js +2 -1
  179. package/modules/currency.js +4 -3
  180. package/modules/dataControllerModule/index.js +4 -3
  181. package/modules/dchain.js +3 -2
  182. package/modules/dianomiBidAdapter.js +17 -14
  183. package/modules/dianomiBidAdapter.md +1 -1
  184. package/modules/feedadBidAdapter.js +40 -8
  185. package/modules/fpdModule/index.js +3 -2
  186. package/modules/iasRtdProvider.js +17 -3
  187. package/modules/ixBidAdapter.js +2 -5
  188. package/modules/liveIntentAnalyticsAdapter.js +148 -0
  189. package/modules/liveIntentAnalyticsAdapter.md +22 -0
  190. package/modules/mass.js +5 -3
  191. package/modules/multibid/index.js +4 -2
  192. package/modules/naveggIdSystem.js +53 -22
  193. package/modules/nextMillenniumBidAdapter.js +168 -55
  194. package/modules/openxOrtbBidAdapter.js +1 -1
  195. package/modules/prebidServerBidAdapter/index.js +18 -4
  196. package/modules/priceFloors.js +5 -4
  197. package/modules/pubCommonId.js +3 -2
  198. package/modules/rtdModule/index.js +3 -2
  199. package/modules/sharethroughBidAdapter.js +8 -6
  200. package/modules/spotxBidAdapter.js +1 -0
  201. package/modules/stroeerCoreBidAdapter.js +129 -92
  202. package/modules/stroeerCoreBidAdapter.md +52 -14
  203. package/modules/teadsBidAdapter.js +32 -10
  204. package/modules/ucfunnelBidAdapter.js +9 -10
  205. package/modules/userId/index.js +73 -48
  206. package/modules/viewability.js +2 -170
  207. package/package.json +3 -3
  208. package/src/adapterManager.js +21 -13
  209. package/src/adapters/bidderFactory.js +28 -6
  210. package/src/auction.js +17 -4
  211. package/src/auctionManager.js +5 -0
  212. package/src/constants.json +2 -0
  213. package/src/native.js +5 -2
  214. package/src/prebid.js +52 -23
  215. package/src/secureCreatives.js +1 -1
  216. package/src/utils/perfMetrics.js +386 -0
  217. package/src/utils.js +1 -1
  218. package/test/helpers/testing-utils.js +4 -3
  219. package/test/spec/modules/adriverIdSystem_spec.js +7 -3
  220. package/test/spec/modules/amxBidAdapter_spec.js +250 -192
  221. package/test/spec/modules/appnexusBidAdapter_spec.js +61 -6
  222. package/test/spec/modules/consentManagementUsp_spec.js +6 -2
  223. package/test/spec/modules/dianomiBidAdapter_spec.js +326 -6
  224. package/test/spec/modules/feedadBidAdapter_spec.js +147 -11
  225. package/test/spec/modules/iasRtdProvider_spec.js +3 -1
  226. package/test/spec/modules/ixBidAdapter_spec.js +0 -64
  227. package/test/spec/modules/liveIntentAnalyticsAdapter_spec.js +297 -0
  228. package/test/spec/modules/nextMillenniumBidAdapter_spec.js +103 -1
  229. package/test/spec/modules/openxOrtbBidAdapter_spec.js +31 -0
  230. package/test/spec/modules/sharethroughBidAdapter_spec.js +23 -2
  231. package/test/spec/modules/spotxBidAdapter_spec.js +6 -0
  232. package/test/spec/modules/stroeerCoreBidAdapter_spec.js +315 -33
  233. package/test/spec/modules/teadsBidAdapter_spec.js +65 -11
  234. package/test/spec/modules/ucfunnelBidAdapter_spec.js +24 -10
  235. package/test/spec/native_spec.js +11 -0
  236. package/test/spec/unit/utils/perfMetrics_spec.js +394 -0
  237. package/wdio.conf.js +1 -0
  238. package/modules/viewability.md +0 -87
  239. package/test/spec/modules/viewability_spec.js +0 -280
package/src/auction.js CHANGED
@@ -77,6 +77,7 @@ import * as events from './events.js'
77
77
  import adapterManager from './adapterManager.js';
78
78
  import CONSTANTS from './constants.json';
79
79
  import {GreedyPromise} from './utils/promise.js';
80
+ import {useMetrics} from './utils/perfMetrics.js';
80
81
 
81
82
  const { syncUsers } = userSync;
82
83
 
@@ -116,7 +117,8 @@ export function resetAuctionState() {
116
117
  * (from getConfig('ortb2') + requestBids({ortb2})) and bidder (a map from bidderCode to ortb2)
117
118
  * @returns {Auction} auction instance
118
119
  */
119
- export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, auctionId, ortb2Fragments}) {
120
+ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, auctionId, ortb2Fragments, metrics}) {
121
+ metrics = useMetrics(metrics);
120
122
  let _adUnits = adUnits;
121
123
  let _labels = labels;
122
124
  let _adUnitCodes = adUnitCodes;
@@ -150,7 +152,8 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
150
152
  noBids: _noBids,
151
153
  bidsReceived: _bidsReceived,
152
154
  winningBids: _winningBids,
153
- timeout: _timeout
155
+ timeout: _timeout,
156
+ metrics: metrics
154
157
  };
155
158
  }
156
159
 
@@ -179,6 +182,9 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
179
182
 
180
183
  _auctionStatus = AUCTION_COMPLETED;
181
184
  _auctionEnd = Date.now();
185
+ metrics.checkpoint('auctionEnd');
186
+ metrics.timeBetween('requestBids', 'auctionEnd', 'requestBids.total');
187
+ metrics.timeBetween('callBids', 'auctionEnd', 'requestBids.callBids');
182
188
 
183
189
  events.emit(CONSTANTS.EVENTS.AUCTION_END, getProperties());
184
190
  bidsBackCallback(_adUnits, function () {
@@ -225,9 +231,12 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
225
231
  _auctionStatus = AUCTION_STARTED;
226
232
  _auctionStart = Date.now();
227
233
 
228
- let bidRequests = adapterManager.makeBidRequests(_adUnits, _auctionStart, _auctionId, _timeout, _labels, ortb2Fragments);
234
+ let bidRequests = metrics.measureTime('requestBids.makeRequests',
235
+ () => adapterManager.makeBidRequests(_adUnits, _auctionStart, _auctionId, _timeout, _labels, ortb2Fragments, metrics));
229
236
  logInfo(`Bids Requested for Auction with id: ${_auctionId}`, bidRequests);
230
237
 
238
+ metrics.checkpoint('callBids')
239
+
231
240
  if (bidRequests.length < 1) {
232
241
  logWarn('No valid bid requests returned for auction');
233
242
  auctionDone();
@@ -358,7 +367,8 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
358
367
  getBidRequests: () => _bidderRequests,
359
368
  getBidsReceived: () => _bidsReceived,
360
369
  getNoBids: () => _noBids,
361
- getFPD: () => ortb2Fragments
370
+ getFPD: () => ortb2Fragments,
371
+ getMetrics: () => metrics,
362
372
  };
363
373
  }
364
374
 
@@ -482,6 +492,9 @@ export function doCallbacksIfTimedout(auctionInstance, bidResponse) {
482
492
  export function addBidToAuction(auctionInstance, bidResponse) {
483
493
  setupBidTargeting(bidResponse);
484
494
 
495
+ const metrics = useMetrics(bidResponse.metrics);
496
+ metrics.timeSince('addBidResponse', 'addBidResponse.total');
497
+
485
498
  events.emit(CONSTANTS.EVENTS.BID_RESPONSE, bidResponse);
486
499
  auctionInstance.addBidReceived(bidResponse);
487
500
 
@@ -24,6 +24,7 @@ import { newAuction, getStandardBidderSettings, AUCTION_COMPLETED } from './auct
24
24
  import {find} from './polyfill.js';
25
25
  import {AuctionIndex} from './auctionIndex.js';
26
26
  import CONSTANTS from './constants.json';
27
+ import {useMetrics} from './utils/perfMetrics.js';
27
28
 
28
29
  /**
29
30
  * Creates new instance of auctionManager. There will only be one instance of auctionManager but
@@ -36,6 +37,10 @@ export function newAuctionManager() {
36
37
  const auctionManager = {};
37
38
 
38
39
  auctionManager.addWinningBid = function(bid) {
40
+ const metrics = useMetrics(bid.metrics);
41
+ metrics.checkpoint('bidWon');
42
+ metrics.timeBetween('auctionEnd', 'bidWon', 'render.pending');
43
+ metrics.timeBetween('requestBids', 'bidWon', 'render.e2e');
39
44
  const auction = find(_auctions, auction => auction.getAuctionId() === bid.auctionId);
40
45
  if (auction) {
41
46
  bid.status = CONSTANTS.BID_STATUS.RENDERED;
@@ -152,6 +152,8 @@
152
152
  "MAIN": 3
153
153
  },
154
154
  "NATIVE_KEYS_THAT_ARE_NOT_ASSETS": [
155
+ "privacyLink",
156
+ "clickUrl",
155
157
  "sendTargetingKeys",
156
158
  "adTemplate",
157
159
  "rendererUrl",
package/src/native.js CHANGED
@@ -80,7 +80,7 @@ const SUPPORTED_TYPES = {
80
80
  image: IMAGE
81
81
  };
82
82
 
83
- const { NATIVE_ASSET_TYPES, NATIVE_IMAGE_TYPES, PREBID_NATIVE_DATA_KEYS_TO_ORTB, NATIVE_KEYS_THAT_ARE_NOT_ASSETS } = CONSTANTS;
83
+ const { NATIVE_ASSET_TYPES, NATIVE_IMAGE_TYPES, PREBID_NATIVE_DATA_KEYS_TO_ORTB, NATIVE_KEYS_THAT_ARE_NOT_ASSETS, NATIVE_KEYS } = CONSTANTS;
84
84
 
85
85
  // inverse native maps useful for converting to legacy
86
86
  const PREBID_NATIVE_DATA_KEYS_TO_ORTB_INVERSE = inverse(PREBID_NATIVE_DATA_KEYS_TO_ORTB);
@@ -488,6 +488,10 @@ export function toOrtbNativeRequest(legacyNativeAssets) {
488
488
  for (let key in legacyNativeAssets) {
489
489
  // skip conversion for non-asset keys
490
490
  if (NATIVE_KEYS_THAT_ARE_NOT_ASSETS.includes(key)) continue;
491
+ if (!NATIVE_KEYS.hasOwnProperty(key)) {
492
+ logError(`Unrecognized native asset code: ${key}. Asset will be ignored.`);
493
+ continue;
494
+ }
491
495
 
492
496
  const asset = legacyNativeAssets[key];
493
497
  let required = 0;
@@ -560,7 +564,6 @@ export function toOrtbNativeRequest(legacyNativeAssets) {
560
564
  // in `ext` case, required field is not needed
561
565
  delete ortbAsset.required;
562
566
  }
563
-
564
567
  ortb.assets.push(ortbAsset);
565
568
  }
566
569
  return ortb;
package/src/prebid.js CHANGED
@@ -1,28 +1,54 @@
1
1
  /** @module pbjs */
2
2
 
3
- import { getGlobal } from './prebidGlobal.js';
3
+ import {getGlobal} from './prebidGlobal.js';
4
4
  import {
5
- adUnitsFilter, flatten, getHighestCpm, isArrayOfNums, isGptPubadsDefined, uniques, logInfo,
6
- contains, logError, isArray, deepClone, deepAccess, isNumber, logWarn, logMessage, isFn,
7
- transformAdServerTargetingObj, bind, replaceAuctionPrice, replaceClickThrough, insertElement,
8
- inIframe, callBurl, createInvisibleIframe, generateUUID, unsupportedBidderMessage, isEmpty, mergeDeep, deepSetValue
5
+ adUnitsFilter,
6
+ bind,
7
+ callBurl,
8
+ contains,
9
+ createInvisibleIframe,
10
+ deepAccess,
11
+ deepClone,
12
+ deepSetValue,
13
+ flatten,
14
+ generateUUID,
15
+ getHighestCpm,
16
+ inIframe,
17
+ insertElement,
18
+ isArray,
19
+ isArrayOfNums,
20
+ isEmpty,
21
+ isFn,
22
+ isGptPubadsDefined,
23
+ isNumber,
24
+ logError,
25
+ logInfo,
26
+ logMessage,
27
+ logWarn,
28
+ mergeDeep,
29
+ replaceAuctionPrice,
30
+ replaceClickThrough,
31
+ transformAdServerTargetingObj,
32
+ uniques,
33
+ unsupportedBidderMessage
9
34
  } from './utils.js';
10
- import { listenMessagesFromCreative } from './secureCreatives.js';
11
- import { userSync } from './userSync.js';
12
- import { config } from './config.js';
13
- import { auctionManager } from './auctionManager.js';
14
- import { filters, targeting } from './targeting.js';
35
+ import {listenMessagesFromCreative} from './secureCreatives.js';
36
+ import {userSync} from './userSync.js';
37
+ import {config} from './config.js';
38
+ import {auctionManager} from './auctionManager.js';
39
+ import {filters, targeting} from './targeting.js';
15
40
  import {hook, wrapHook} from './hook.js';
16
- import { loadSession } from './debugging.js';
41
+ import {loadSession} from './debugging.js';
17
42
  import {includes} from './polyfill.js';
18
- import { adunitCounter } from './adUnits.js';
19
- import { executeRenderer, isRendererRequired } from './Renderer.js';
20
- import { createBid } from './bidfactory.js';
21
- import { storageCallbacks } from './storageManager.js';
22
- import { emitAdRenderSucceeded, emitAdRenderFail } from './adRendering.js';
23
- import {gdprDataHandler, getS2SBidderSet, uspDataHandler, default as adapterManager} from './adapterManager.js';
43
+ import {adunitCounter} from './adUnits.js';
44
+ import {executeRenderer, isRendererRequired} from './Renderer.js';
45
+ import {createBid} from './bidfactory.js';
46
+ import {storageCallbacks} from './storageManager.js';
47
+ import {emitAdRenderFail, emitAdRenderSucceeded} from './adRendering.js';
48
+ import {default as adapterManager, gdprDataHandler, getS2SBidderSet, uspDataHandler} from './adapterManager.js';
24
49
  import CONSTANTS from './constants.json';
25
- import * as events from './events.js'
50
+ import * as events from './events.js';
51
+ import {newMetrics, useMetrics} from './utils/perfMetrics.js';
26
52
 
27
53
  const $$PREBID_GLOBAL$$ = getGlobal();
28
54
  const { triggerUserSyncs } = userSync;
@@ -584,7 +610,7 @@ $$PREBID_GLOBAL$$.removeAdUnit = function (adUnitCode) {
584
610
  * @alias module:pbjs.requestBids
585
611
  */
586
612
  $$PREBID_GLOBAL$$.requestBids = (function() {
587
- const delegate = hook('async', function ({ bidsBackHandler, timeout, adUnits, adUnitCodes, labels, auctionId, ortb2 } = {}) {
613
+ const delegate = hook('async', function ({ bidsBackHandler, timeout, adUnits, adUnitCodes, labels, auctionId, ortb2, metrics } = {}) {
588
614
  events.emit(REQUEST_BIDS);
589
615
  const cbTimeout = timeout || config.getConfig('bidderTimeout');
590
616
  logInfo('Invoking $$PREBID_GLOBAL$$.requestBids', arguments);
@@ -592,7 +618,7 @@ $$PREBID_GLOBAL$$.requestBids = (function() {
592
618
  global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}),
593
619
  bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, cfg.ortb2]).filter(([_, ortb2]) => ortb2 != null))
594
620
  }
595
- return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ortb2Fragments});
621
+ return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ortb2Fragments, metrics});
596
622
  }, 'requestBids');
597
623
 
598
624
  return wrapHook(delegate, function requestBids(req = {}) {
@@ -600,15 +626,17 @@ $$PREBID_GLOBAL$$.requestBids = (function() {
600
626
  // any hook has a chance to run.
601
627
  // otherwise, if the caller goes on to use addAdUnits/removeAdUnits, any asynchronous logic
602
628
  // in any hook might see their effects.
629
+ req.metrics = newMetrics();
630
+ req.metrics.checkpoint('requestBids');
603
631
  let adUnits = req.adUnits || $$PREBID_GLOBAL$$.adUnits;
604
632
  req.adUnits = (isArray(adUnits) ? adUnits.slice() : [adUnits]);
605
633
  return delegate.call(this, req);
606
634
  });
607
635
  })();
608
636
 
609
- export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ortb2Fragments } = {}) {
637
+ export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ortb2Fragments, metrics } = {}) {
610
638
  const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []);
611
- adUnits = checkAdUnitSetup(adUnits);
639
+ adUnits = useMetrics(metrics).measureTime('requestBids.validate', () => checkAdUnitSetup(adUnits));
612
640
 
613
641
  if (adUnitCodes && adUnitCodes.length) {
614
642
  // if specific adUnitCodes supplied filter adUnits for those codes
@@ -678,7 +706,8 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout:
678
706
  cbTimeout,
679
707
  labels,
680
708
  auctionId,
681
- ortb2Fragments
709
+ ortb2Fragments,
710
+ metrics,
682
711
  });
683
712
 
684
713
  let adUnitsLen = adUnits.length;
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import * as events from './events.js';
7
- import { fireNativeTrackers, getAssetMessage, getAllAssetsMessage } from './native.js';
7
+ import {fireNativeTrackers, getAllAssetsMessage, getAssetMessage} from './native.js';
8
8
  import constants from './constants.json';
9
9
  import {deepAccess, isApnGetTagDefined, isGptPubadsDefined, logError, logWarn, replaceAuctionPrice} from './utils.js';
10
10
  import {auctionManager} from './auctionManager.js';
@@ -0,0 +1,386 @@
1
+ import {config} from '../config.js';
2
+ export const CONFIG_TOGGLE = 'performanceMetrics';
3
+ const getTime = window.performance && window.performance.now ? () => window.performance.now() : () => Date.now();
4
+ const NODES = new WeakMap();
5
+
6
+ export function metricsFactory({now = getTime, mkNode = makeNode, mkTimer = makeTimer, mkRenamer = (rename) => rename, nodes = NODES} = {}) {
7
+ return function newMetrics() {
8
+ function makeMetrics(self, rename = (n) => ({forEach(fn) { fn(n); }})) {
9
+ rename = mkRenamer(rename);
10
+
11
+ function accessor(slot) {
12
+ return function (name) {
13
+ return self.dfWalk({
14
+ visit(edge, node) {
15
+ const obj = node[slot];
16
+ if (obj.hasOwnProperty(name)) {
17
+ return obj[name];
18
+ }
19
+ }
20
+ });
21
+ };
22
+ }
23
+
24
+ const getTimestamp = accessor('timestamps');
25
+
26
+ /**
27
+ * Register a metric.
28
+ *
29
+ * @param name metric name
30
+ * @param value metric valiue
31
+ */
32
+ function setMetric(name, value) {
33
+ const names = rename(name);
34
+ self.dfWalk({
35
+ follow(inEdge, outEdge) {
36
+ return outEdge.propagate && (!inEdge || !inEdge.stopPropagation)
37
+ },
38
+ visit(edge, node) {
39
+ names.forEach(name => {
40
+ if (edge == null) {
41
+ node.metrics[name] = value;
42
+ } else {
43
+ if (!node.groups.hasOwnProperty(name)) {
44
+ node.groups[name] = [];
45
+ }
46
+ node.groups[name].push(value);
47
+ }
48
+ })
49
+ }
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Mark the current time as a checkpoint with the given name, to be referenced later
55
+ * by `timeSince` or `timeBetween`.
56
+ *
57
+ * @param name checkpoint name
58
+ */
59
+ function checkpoint(name) {
60
+ self.timestamps[name] = now();
61
+ }
62
+
63
+ /**
64
+ * Get the tame passed since `checkpoint`, and optionally save it as a metric.
65
+ *
66
+ * @param checkpoint checkpoint name
67
+ * @param metric? metric name
68
+ * @return {number} time between now and `checkpoint`
69
+ */
70
+ function timeSince(checkpoint, metric) {
71
+ const ts = getTimestamp(checkpoint);
72
+ const elapsed = ts != null ? now() - ts : null;
73
+ if (metric != null) {
74
+ setMetric(metric, elapsed);
75
+ }
76
+ return elapsed;
77
+ }
78
+
79
+ /**
80
+ * Get the time passed between `startCheckpoint` and `endCheckpoint`, optionally saving it as a metric.
81
+ *
82
+ * @param startCheckpoint begin checkpoint
83
+ * @param endCheckpoint end checkpoint
84
+ * @param metric? metric name
85
+ * @return {number} time passed between `startCheckpoint` and `endCheckpoint`
86
+ */
87
+ function timeBetween(startCheckpoint, endCheckpoint, metric) {
88
+ const start = getTimestamp(startCheckpoint);
89
+ const end = getTimestamp(endCheckpoint);
90
+ const elapsed = start != null && end != null ? end - start : null;
91
+ if (metric != null) {
92
+ setMetric(metric, elapsed);
93
+ }
94
+ return elapsed;
95
+ }
96
+
97
+ /**
98
+ * A function that, when called, stops a time measure and saves it as a metric.
99
+ *
100
+ * @typedef {function(): void} MetricsTimer
101
+ * @template {function} F
102
+ * @property {function(F): F} stopBefore returns a wrapper around the given function that begins by
103
+ * stopping this time measure.
104
+ * @property {function(F): F} stopAfter returns a wrapper around the given function that ends by
105
+ * stopping this time measure.
106
+ */
107
+
108
+ /**
109
+ * Start measuring a time metric with the given name.
110
+ *
111
+ * @param name metric name
112
+ * @return {MetricsTimer}
113
+ */
114
+ function startTiming(name) {
115
+ return mkTimer(now, (val) => setMetric(name, val))
116
+ }
117
+
118
+ /**
119
+ * Run fn and measure the time spent in it.
120
+ *
121
+ * @template T
122
+ * @param name the name to use for the measured time metric
123
+ * @param {function(): T} fn
124
+ * @return {T} the return value of `fn`
125
+ */
126
+ function measureTime(name, fn) {
127
+ return startTiming(name).stopAfter(fn)();
128
+ }
129
+
130
+ /**
131
+ * @typedef {function: T} HookFn
132
+ * @property {function(T): void} bail
133
+ *
134
+ * @template T
135
+ * @typedef {T: HookFn} TimedHookFn
136
+ * @property {function(): void} stopTiming
137
+ * @property {T} untimed
138
+ */
139
+
140
+ /**
141
+ * Convenience method for measuring time spent in a `.before` or `.after` hook.
142
+ *
143
+ * @template T
144
+ * @param name metric name
145
+ * @param {HookFn} next the hook's `next` (first) argument
146
+ * @param {function(TimedHookFn): T} fn a function that will be run immediately; it takes `next`,
147
+ * where both `next` and `next.bail` automatically
148
+ * call `stopTiming` before continuing with the original hook.
149
+ * @return {T} fn's return value
150
+ */
151
+ function measureHookTime(name, next, fn) {
152
+ const stopTiming = startTiming(name);
153
+ return fn((function (orig) {
154
+ const next = stopTiming.stopBefore(orig);
155
+ next.bail = orig.bail && stopTiming.stopBefore(orig.bail);
156
+ next.stopTiming = stopTiming;
157
+ next.untimed = orig;
158
+ return next;
159
+ })(next));
160
+ }
161
+
162
+ /**
163
+ * Get all registered metrics.
164
+ * @return {{}}
165
+ */
166
+ function getMetrics() {
167
+ let result = {}
168
+ self.dfWalk({
169
+ visit(edge, node) {
170
+ result = Object.assign({}, !edge || edge.includeGroups ? node.groups : null, node.metrics, result);
171
+ }
172
+ });
173
+ return result;
174
+ }
175
+
176
+ /**
177
+ * Create and return a new metrics object that starts as a view on all metrics registered here,
178
+ * and - by default - also propagates all new metrics here.
179
+ *
180
+ * Propagated metrics are grouped together, and intended for repeated operations. For example, with the following:
181
+ *
182
+ * ```
183
+ * const metrics = newMetrics();
184
+ * const requests = metrics.measureTime('buildRequests', buildRequests)
185
+ * requests.forEach((req) => {
186
+ * const requestMetrics = metrics.fork();
187
+ * requestMetrics.measureTime('processRequest', () => processRequest(req);
188
+ * })
189
+ * ```
190
+ *
191
+ * if `buildRequests` takes 10ms and returns 3 objects, which respectively take 100, 200, and 300ms in `processRequest`, then
192
+ * the final `metrics.getMetrics()` would be:
193
+ *
194
+ * ```
195
+ * {
196
+ * buildRequests: 10,
197
+ * processRequest: [100, 200, 300]
198
+ * }
199
+ * ```
200
+ *
201
+ * while the inner `requestMetrics.getMetrics()` would be:
202
+ *
203
+ * ```
204
+ * {
205
+ * buildRequests: 10,
206
+ * processRequest: 100 // or 200 for the 2nd loop, etc
207
+ * }
208
+ * ```
209
+ *
210
+ *
211
+ * @param propagate if false, the forked metrics will not be propagated here
212
+ * @param stopPropagation if true, propagation from the new metrics is stopped here - instead of
213
+ * continuing up the chain (if for example these metrics were themselves created through `.fork()`)
214
+ * @param includeGroups if true, the forked metrics will also replicate metrics that were propagated
215
+ * here from elsewhere. For example:
216
+ * ```
217
+ * const metrics = newMetrics();
218
+ * const op1 = metrics.fork();
219
+ * const withoutGroups = metrics.fork();
220
+ * const withGroups = metrics.fork({includeGroups: true});
221
+ * op1.setMetric('foo', 'bar');
222
+ * withoutGroups.getMetrics() // {}
223
+ * withGroups.getMetrics() // {foo: ['bar']}
224
+ * ```
225
+ */
226
+ function fork({propagate = true, stopPropagation = false, includeGroups = false} = {}) {
227
+ return makeMetrics(mkNode([[self, {propagate, stopPropagation, includeGroups}]]), rename);
228
+ }
229
+
230
+ /**
231
+ * Join `otherMetrics` with these; all metrics from `otherMetrics` will (by default) be propagated here,
232
+ * and all metrics from here will be included in `otherMetrics`.
233
+ *
234
+ * `propagate`, `stopPropagation` and `includeGroups` have the same semantics as in `.fork()`.
235
+ */
236
+ function join(otherMetrics, {propagate = true, stopPropagation = false, includeGroups = false} = {}) {
237
+ const other = nodes.get(otherMetrics);
238
+ if (other != null) {
239
+ other.addParent(self, {propagate, stopPropagation, includeGroups});
240
+ }
241
+ }
242
+
243
+ /**
244
+ * return a version of these metrics where all new metrics are renamed according to `renameFn`.
245
+ *
246
+ * @param {function(String): Array[String]} renameFn
247
+ */
248
+ function renameWith(renameFn) {
249
+ return makeMetrics(self, renameFn);
250
+ }
251
+
252
+ /**
253
+ * Create a new metrics object that uses the same propagation and renaming rules as this one.
254
+ */
255
+ function newMetrics() {
256
+ return makeMetrics(self.newSibling(), rename);
257
+ }
258
+
259
+ const metrics = {
260
+ startTiming,
261
+ measureTime,
262
+ measureHookTime,
263
+ checkpoint,
264
+ timeSince,
265
+ timeBetween,
266
+ setMetric,
267
+ getMetrics,
268
+ fork,
269
+ join,
270
+ newMetrics,
271
+ renameWith,
272
+ toJSON() {
273
+ return getMetrics();
274
+ }
275
+ };
276
+ nodes.set(metrics, self);
277
+ return metrics;
278
+ }
279
+
280
+ return makeMetrics(mkNode([]));
281
+ }
282
+ }
283
+
284
+ function wrapFn(fn, before, after) {
285
+ return function () {
286
+ before && before();
287
+ try {
288
+ return fn.apply(this, arguments);
289
+ } finally {
290
+ after && after();
291
+ }
292
+ };
293
+ }
294
+
295
+ function makeTimer(now, cb) {
296
+ const start = now();
297
+ let done = false;
298
+ function stopTiming() {
299
+ if (!done) {
300
+ // eslint-disable-next-line standard/no-callback-literal
301
+ cb(now() - start);
302
+ done = true;
303
+ }
304
+ }
305
+ stopTiming.stopBefore = (fn) => wrapFn(fn, stopTiming);
306
+ stopTiming.stopAfter = (fn) => wrapFn(fn, null, stopTiming);
307
+ return stopTiming;
308
+ }
309
+
310
+ function makeNode(parents) {
311
+ return {
312
+ metrics: {},
313
+ timestamps: {},
314
+ groups: {},
315
+ addParent(node, edge) {
316
+ parents.push([node, edge]);
317
+ },
318
+ newSibling() {
319
+ return makeNode(parents.slice());
320
+ },
321
+ dfWalk({visit, follow = () => true, visited = new Set(), inEdge} = {}) {
322
+ let res;
323
+ if (!visited.has(this)) {
324
+ visited.add(this);
325
+ res = visit(inEdge, this);
326
+ if (res != null) return res;
327
+ for (const [parent, outEdge] of parents) {
328
+ if (follow(inEdge, outEdge)) {
329
+ res = parent.dfWalk({visit, follow, visited, inEdge: outEdge});
330
+ if (res != null) return res;
331
+ }
332
+ }
333
+ }
334
+ }
335
+ };
336
+ }
337
+
338
+ const nullMetrics = (() => {
339
+ const nop = function () {};
340
+ const empty = () => ({});
341
+ const none = {forEach: nop};
342
+ const nullTimer = () => null;
343
+ nullTimer.stopBefore = (fn) => fn;
344
+ nullTimer.stopAfter = (fn) => fn;
345
+ const nullNode = Object.defineProperties(
346
+ {dfWalk: nop, newSibling: () => nullNode, addParent: nop},
347
+ Object.fromEntries(['metrics', 'timestamps', 'groups'].map(prop => [prop, {get: empty}])));
348
+ return metricsFactory({
349
+ now: () => 0,
350
+ mkNode: () => nullNode,
351
+ mkRenamer: () => () => none,
352
+ mkTimer: () => nullTimer,
353
+ nodes: {get: nop, set: nop}
354
+ })();
355
+ })();
356
+
357
+ let enabled = true;
358
+ config.getConfig(CONFIG_TOGGLE, (cfg) => { enabled = !!cfg[CONFIG_TOGGLE] });
359
+
360
+ /**
361
+ * convenience fallback function for metrics that may be undefined, especially during tests.
362
+ */
363
+ export function useMetrics(metrics) {
364
+ return (enabled && metrics) || nullMetrics;
365
+ }
366
+
367
+ export const newMetrics = (() => {
368
+ const makeMetrics = metricsFactory();
369
+ return function () {
370
+ return enabled ? makeMetrics() : nullMetrics;
371
+ }
372
+ })();
373
+
374
+ export function hookTimer(prefix, getMetrics) {
375
+ return function(name, hookFn) {
376
+ return function (next, ...args) {
377
+ const that = this;
378
+ return useMetrics(getMetrics.apply(that, args)).measureHookTime(prefix + name, next, function (next) {
379
+ return hookFn.call(that, next, ...args);
380
+ });
381
+ }
382
+ }
383
+ }
384
+
385
+ export const timedAuctionHook = hookTimer('requestBids.', (req) => req.metrics);
386
+ export const timedBidResponseHook = hookTimer('addBidResponse.', (_, bid) => bid.metrics)
package/src/utils.js CHANGED
@@ -4,7 +4,7 @@ import {find, includes} from './polyfill.js';
4
4
  import CONSTANTS from './constants.json';
5
5
  import {GreedyPromise} from './utils/promise.js';
6
6
  export { default as deepAccess } from 'dlv/index.js';
7
- export { default as deepSetValue } from 'dset';
7
+ export { dset as deepSetValue } from 'dset';
8
8
 
9
9
  var tArr = 'Array';
10
10
  var tStr = 'String';
@@ -1,12 +1,13 @@
1
1
  /* eslint-disable no-console */
2
2
  const {expect} = require('chai');
3
+ const DEFAULT_TIMEOUT = 2000;
3
4
  const utils = {
4
5
  host: (process.env.TEST_SERVER_HOST) ? process.env.TEST_SERVER_HOST : 'localhost',
5
6
  protocol: (process.env.TEST_SERVER_PROTOCOL) ? 'https' : 'http',
6
7
  testPageURL: function(name) {
7
8
  return `${utils.protocol}://${utils.host}:9999/test/pages/${name}`
8
9
  },
9
- waitForElement: function(elementRef, time = 2000) {
10
+ waitForElement: function(elementRef, time = DEFAULT_TIMEOUT) {
10
11
  let element = $(elementRef);
11
12
  element.waitForExist({timeout: time});
12
13
  },
@@ -14,7 +15,7 @@ const utils = {
14
15
  let iframe = $(frameRef);
15
16
  browser.switchToFrame(iframe);
16
17
  },
17
- loadAndWaitForElement(url, selector, pause = 3000, timeout = 2000, retries = 3, attempt = 1) {
18
+ loadAndWaitForElement(url, selector, pause = 3000, timeout = DEFAULT_TIMEOUT, retries = 3, attempt = 1) {
18
19
  browser.url(url);
19
20
  browser.pause(pause);
20
21
  if (selector != null) {
@@ -27,7 +28,7 @@ const utils = {
27
28
  }
28
29
  }
29
30
  },
30
- setupTest({url, waitFor, expectGAMCreative = null, pause = 3000, timeout = 2000, retries = 3}, name, fn) {
31
+ setupTest({url, waitFor, expectGAMCreative = null, pause = 3000, timeout = DEFAULT_TIMEOUT, retries = 3}, name, fn) {
31
32
  describe(name, function () {
32
33
  this.retries(retries);
33
34
  before(() => utils.loadAndWaitForElement(url, waitFor, pause, timeout, retries));