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
@@ -154,6 +154,7 @@ import {
154
154
  import {getPPID as coreGetPPID} from '../../src/adserver.js';
155
155
  import {defer, GreedyPromise} from '../../src/utils/promise.js';
156
156
  import {hasPurpose1Consent} from '../../src/utils/gpdr.js';
157
+ import {newMetrics, timedAuctionHook, useMetrics} from '../../src/utils/perfMetrics.js';
157
158
 
158
159
  const MODULE_NAME = 'User ID';
159
160
  const COOKIE = 'cookie';
@@ -196,6 +197,20 @@ let ppidSource;
196
197
 
197
198
  let configListener;
198
199
 
200
+ const uidMetrics = (() => {
201
+ let metrics;
202
+ return () => {
203
+ if (metrics == null) {
204
+ metrics = newMetrics();
205
+ }
206
+ return metrics;
207
+ }
208
+ })();
209
+
210
+ function submoduleMetrics(moduleName) {
211
+ return uidMetrics().fork().renameWith(n => [`userId.mod.${n}`, `userId.mods.${moduleName}.${n}`])
212
+ }
213
+
199
214
  /** @param {Submodule[]} submodules */
200
215
  export function setSubmoduleRegistry(submodules) {
201
216
  submoduleRegistry = submodules;
@@ -395,14 +410,13 @@ export function findRootDomain(fullDomain = window.location.hostname) {
395
410
  * @param {function} cb - callback for after processing is done.
396
411
  */
397
412
  function processSubmoduleCallbacks(submodules, cb) {
398
- let done = () => {};
399
- if (cb) {
400
- done = delayExecution(() => {
401
- clearTimeout(timeoutID);
402
- cb();
403
- }, submodules.length);
404
- }
413
+ cb = uidMetrics().fork().startTiming('userId.callbacks.total').stopBefore(cb);
414
+ const done = delayExecution(() => {
415
+ clearTimeout(timeoutID);
416
+ cb();
417
+ }, submodules.length);
405
418
  submodules.forEach(function (submodule) {
419
+ const moduleDone = submoduleMetrics(submodule.submodule.name).startTiming('callback').stopBefore(done);
406
420
  function callbackCompleted(idObj) {
407
421
  // if valid, id data should be saved to cookie/html storage
408
422
  if (idObj) {
@@ -414,13 +428,13 @@ function processSubmoduleCallbacks(submodules, cb) {
414
428
  } else {
415
429
  logInfo(`${MODULE_NAME}: ${submodule.submodule.name} - request id responded with an empty value`);
416
430
  }
417
- done();
431
+ moduleDone();
418
432
  }
419
433
  try {
420
434
  submodule.callback(callbackCompleted);
421
435
  } catch (e) {
422
436
  logError(`Error in userID module '${submodule.submodule.name}':`, e);
423
- done();
437
+ moduleDone();
424
438
  }
425
439
  // clear callback, this prop is used to test if all submodule callbacks are complete below
426
440
  submodule.callback = undefined;
@@ -508,13 +522,16 @@ function idSystemInitializer({delay = GreedyPromise.timeout} = {}) {
508
522
  const startCallbacks = defer();
509
523
  let cancel;
510
524
  let initialized = false;
525
+ let initMetrics;
511
526
 
512
527
  function cancelAndTry(promise) {
528
+ initMetrics = uidMetrics().fork();
513
529
  if (cancel != null) {
514
530
  cancel.reject(INIT_CANCELED);
515
531
  }
516
532
  cancel = defer();
517
- return GreedyPromise.race([promise, cancel.promise]);
533
+ return GreedyPromise.race([promise, cancel.promise])
534
+ .finally(initMetrics.startTiming('userId.total'))
518
535
  }
519
536
 
520
537
  // grab a reference to global vars so that the promise chains remain isolated;
@@ -532,13 +549,17 @@ function idSystemInitializer({delay = GreedyPromise.timeout} = {}) {
532
549
  }
533
550
  }
534
551
 
552
+ function timeGdpr() {
553
+ return gdprDataHandler.promise.finally(initMetrics.startTiming('userId.init.gdpr'));
554
+ }
555
+
535
556
  let done = cancelAndTry(
536
557
  GreedyPromise.all([hooksReady, startInit.promise])
537
- .then(() => gdprDataHandler.promise)
558
+ .then(timeGdpr)
538
559
  .then(checkRefs((consentData) => {
539
560
  initSubmodules(initModules, allModules, consentData);
540
561
  }))
541
- .then(() => startCallbacks.promise)
562
+ .then(() => startCallbacks.promise.finally(initMetrics.startTiming('userId.callbacks.pending')))
542
563
  .then(checkRefs(() => {
543
564
  const modWithCb = initModules.filter(item => isFn(item.callback));
544
565
  if (modWithCb.length) {
@@ -570,7 +591,7 @@ function idSystemInitializer({delay = GreedyPromise.timeout} = {}) {
570
591
  done = cancelAndTry(
571
592
  done
572
593
  .catch(() => null)
573
- .then(() => gdprDataHandler.promise) // fetch again in case a refresh was forced before this was resolved
594
+ .then(timeGdpr) // fetch again in case a refresh was forced before this was resolved
574
595
  .then(checkRefs((consentData) => {
575
596
  const cbModules = initSubmodules(
576
597
  initModules,
@@ -614,7 +635,7 @@ function getPPID() {
614
635
  * @param {Object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids.
615
636
  * @param {function} fn required; The next function in the chain, used by hook.js
616
637
  */
617
- export function requestBidsHook(fn, reqBidsConfigObj, {delay = GreedyPromise.timeout, getIds = getUserIdsAsync} = {}) {
638
+ export const requestBidsHook = timedAuctionHook('userId', function requestBidsHook(fn, reqBidsConfigObj, {delay = GreedyPromise.timeout, getIds = getUserIdsAsync} = {}) {
618
639
  GreedyPromise.race([
619
640
  getIds().catch(() => null),
620
641
  delay(auctionDelay)
@@ -633,11 +654,11 @@ export function requestBidsHook(fn, reqBidsConfigObj, {delay = GreedyPromise.tim
633
654
  });
634
655
  }
635
656
  }
636
-
657
+ uidMetrics().join(useMetrics(reqBidsConfigObj.metrics), {propagate: false, includeGroups: true});
637
658
  // calling fn allows prebid to continue processing
638
659
  fn.call(this, reqBidsConfigObj);
639
660
  });
640
- }
661
+ });
641
662
 
642
663
  /**
643
664
  * This function will be exposed in global-name-space so that userIds stored by Prebid UserId module can be used by external codes as well.
@@ -835,43 +856,47 @@ function populateSubmoduleId(submodule, consentData, storedConsentData, forceRef
835
856
  }
836
857
 
837
858
  function initSubmodules(dest, submodules, consentData, forceRefresh = false) {
838
- if (!submodules.length) return []; // to simplify log messages from here on
859
+ return uidMetrics().fork().measureTime('userId.init.modules', function () {
860
+ if (!submodules.length) return []; // to simplify log messages from here on
839
861
 
840
- // filter out submodules whose storage type is not enabled
841
- // this needs to be done here (after consent data has loaded) so that enforcement may disable storage globally
842
- const storageTypes = getActiveStorageTypes();
843
- submodules = submodules.filter((submod) => !submod.config.storage || storageTypes.has(submod.config.storage.type));
844
-
845
- if (!submodules.length) {
846
- logWarn(`${MODULE_NAME} - no ID module is configured for one of the available storage types:`, Array.from(storageTypes));
847
- return [];
848
- }
862
+ // filter out submodules whose storage type is not enabled
863
+ // this needs to be done here (after consent data has loaded) so that enforcement may disable storage globally
864
+ const storageTypes = getActiveStorageTypes();
865
+ submodules = submodules.filter((submod) => !submod.config.storage || storageTypes.has(submod.config.storage.type));
849
866
 
850
- // another consent check, this time each module is checked for consent with its own gvlid
851
- let { userIdModules, hasValidated } = validateGdprEnforcement(submodules, consentData);
852
- if (!hasValidated && !hasPurpose1Consent(consentData)) {
853
- logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`);
854
- return [];
855
- }
867
+ if (!submodules.length) {
868
+ logWarn(`${MODULE_NAME} - no ID module is configured for one of the available storage types:`, Array.from(storageTypes));
869
+ return [];
870
+ }
856
871
 
857
- // we always want the latest consentData stored, even if we don't execute any submodules
858
- const storedConsentData = getStoredConsentData();
859
- setStoredConsentData(consentData);
872
+ // another consent check, this time each module is checked for consent with its own gvlid
873
+ let { userIdModules, hasValidated } = validateGdprEnforcement(submodules, consentData);
874
+ if (!hasValidated && !hasPurpose1Consent(consentData)) {
875
+ logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`);
876
+ return [];
877
+ }
860
878
 
861
- const initialized = userIdModules.reduce((carry, submodule) => {
862
- try {
863
- populateSubmoduleId(submodule, consentData, storedConsentData, forceRefresh);
864
- carry.push(submodule);
865
- } catch (e) {
866
- logError(`Error in userID module '${submodule.submodule.name}':`, e);
879
+ // we always want the latest consentData stored, even if we don't execute any submodules
880
+ const storedConsentData = getStoredConsentData();
881
+ setStoredConsentData(consentData);
882
+
883
+ const initialized = userIdModules.reduce((carry, submodule) => {
884
+ return submoduleMetrics(submodule.submodule.name).measureTime('init', () => {
885
+ try {
886
+ populateSubmoduleId(submodule, consentData, storedConsentData, forceRefresh);
887
+ carry.push(submodule);
888
+ } catch (e) {
889
+ logError(`Error in userID module '${submodule.submodule.name}':`, e);
890
+ }
891
+ return carry;
892
+ })
893
+ }, []);
894
+ if (initialized.length) {
895
+ setPrebidServerEidPermissions(initialized);
867
896
  }
868
- return carry;
869
- }, []);
870
- if (initialized.length) {
871
- setPrebidServerEidPermissions(initialized);
872
- }
873
- initialized.forEach(updateInitializedSubmodules.bind(null, dest));
874
- return initialized;
897
+ initialized.forEach(updateInitializedSubmodules.bind(null, dest));
898
+ return initialized;
899
+ })
875
900
  }
876
901
 
877
902
  function updateInitializedSubmodules(dest, submodule) {
@@ -1,177 +1,9 @@
1
- import {insertHtmlIntoIframe, isFn, isStr, logInfo, logWarn, triggerPixel} from '../src/utils.js';
2
- import {getGlobal} from '../src/prebidGlobal.js';
3
- import {find} from '../src/polyfill.js';
1
+ import {logWarn} from '../src/utils.js';
4
2
 
5
3
  export const MODULE_NAME = 'viewability';
6
4
 
7
5
  export function init() {
8
- (getGlobal()).viewability = {
9
- startMeasurement: startMeasurement,
10
- stopMeasurement: stopMeasurement,
11
- };
12
-
13
- listenMessagesFromCreative();
14
- }
15
-
16
- const observers = new Map();
17
-
18
- function isValid(vid, element, tracker, criteria) {
19
- if (!element) {
20
- logWarn(`${MODULE_NAME}: no html element provided`);
21
- return false;
22
- }
23
-
24
- let validTracker = tracker &&
25
- ((tracker.method === 'img' && isStr(tracker.value)) ||
26
- (tracker.method === 'js' && isStr(tracker.value)) ||
27
- (tracker.method === 'callback' && isFn(tracker.value)));
28
-
29
- if (!validTracker) {
30
- logWarn(`${MODULE_NAME}: invalid tracker`, tracker);
31
- return false;
32
- }
33
-
34
- if (!criteria || !criteria.inViewThreshold || !criteria.timeInView) {
35
- logWarn(`${MODULE_NAME}: missing criteria`, criteria);
36
- return false;
37
- }
38
-
39
- if (!vid || observers.has(vid)) {
40
- logWarn(`${MODULE_NAME}: must provide an unregistered vid`, vid);
41
- return false;
42
- }
43
-
44
- return true;
45
- }
46
-
47
- function stopObserving(observer, vid, element) {
48
- observer.unobserve(element);
49
- observers.get(vid).done = true;
50
- }
51
-
52
- function fireViewabilityTracker(element, tracker) {
53
- switch (tracker.method) {
54
- case 'img':
55
- triggerPixel(tracker.value, () => {
56
- logInfo(`${MODULE_NAME}: viewability pixel fired`, tracker.value);
57
- });
58
- break;
59
- case 'js':
60
- insertHtmlIntoIframe(`<script src="${tracker.value}"></script>`);
61
- break;
62
- case 'callback':
63
- tracker.value(element);
64
- break;
65
- }
66
- }
67
-
68
- function viewabilityCriteriaMet(observer, vid, element, tracker) {
69
- stopObserving(observer, vid, element);
70
- fireViewabilityTracker(element, tracker);
71
- }
72
-
73
- /**
74
- * Start measuring viewability of an element
75
- * @typedef {{ method: string='img','js','callback', value: string|function }} ViewabilityTracker { method: 'img', value: 'http://my.tracker/123' }
76
- * @typedef {{ inViewThreshold: number, timeInView: number }} ViewabilityCriteria { inViewThreshold: 0.5, timeInView: 1000 }
77
- * @param {string} vid unique viewability identifier
78
- * @param {HTMLElement} element
79
- * @param {ViewabilityTracker} tracker
80
- * @param {ViewabilityCriteria} criteria
81
- */
82
- export function startMeasurement(vid, element, tracker, criteria) {
83
- if (!isValid(vid, element, tracker, criteria)) {
84
- return;
85
- }
86
-
87
- const options = {
88
- root: null,
89
- rootMargin: '0px',
90
- threshold: criteria.inViewThreshold,
91
- };
92
-
93
- let observer;
94
- let viewable = false;
95
- let stateChange = (entries) => {
96
- viewable = entries[0].isIntersecting;
97
-
98
- if (viewable) {
99
- observers.get(vid).timeoutId = window.setTimeout(() => {
100
- viewabilityCriteriaMet(observer, vid, element, tracker);
101
- }, criteria.timeInView);
102
- } else if (observers.get(vid).timeoutId) {
103
- window.clearTimeout(observers.get(vid).timeoutId);
104
- }
105
- };
106
-
107
- observer = new IntersectionObserver(stateChange, options);
108
- observers.set(vid, {
109
- observer: observer,
110
- element: element,
111
- timeoutId: null,
112
- done: false,
113
- });
114
-
115
- observer.observe(element);
116
-
117
- logInfo(`${MODULE_NAME}: startMeasurement called with:`, arguments);
118
- }
119
-
120
- /**
121
- * Stop measuring viewability of an element
122
- * @param {string} vid unique viewability identifier
123
- */
124
- export function stopMeasurement(vid) {
125
- if (!vid || !observers.has(vid)) {
126
- logWarn(`${MODULE_NAME}: must provide a registered vid`, vid);
127
- return;
128
- }
129
-
130
- observers.get(vid).observer.unobserve(observers.get(vid).element);
131
- if (observers.get(vid).timeoutId) {
132
- window.clearTimeout(observers.get(vid).timeoutId);
133
- }
134
-
135
- // allow the observer under this vid to be created again
136
- if (!observers.get(vid).done) {
137
- observers.delete(vid);
138
- }
139
- }
140
-
141
- function listenMessagesFromCreative() {
142
- window.addEventListener('message', receiveMessage, false);
143
- }
144
-
145
- /**
146
- * Recieve messages from creatives
147
- * @param {MessageEvent} evt
148
- */
149
- export function receiveMessage(evt) {
150
- var key = evt.message ? 'message' : 'data';
151
- var data = {};
152
- try {
153
- data = JSON.parse(evt[key]);
154
- } catch (e) {
155
- return;
156
- }
157
-
158
- if (!data || data.message !== 'Prebid Viewability') {
159
- return;
160
- }
161
-
162
- switch (data.action) {
163
- case 'startMeasurement':
164
- let element = data.elementId && document.getElementById(data.elementId);
165
- if (!element) {
166
- element = find(document.getElementsByTagName('IFRAME'), iframe => (iframe.contentWindow || iframe.contentDocument.defaultView) == evt.source);
167
- }
168
-
169
- startMeasurement(data.vid, element, data.tracker, data.criteria);
170
- break;
171
- case 'stopMeasurement':
172
- stopMeasurement(data.vid);
173
- break;
174
- }
6
+ logWarn('Viewability module should not be used. See https://github.com/prebid/Prebid.js/issues/8928');
175
7
  }
176
8
 
177
9
  init();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prebid.js",
3
- "version": "7.14.0",
3
+ "version": "7.16.0",
4
4
  "description": "Header Bidding Management Library",
5
5
  "main": "src/prebid.js",
6
6
  "scripts": {
@@ -67,7 +67,7 @@
67
67
  "gulp-clean": "^0.3.2",
68
68
  "gulp-concat": "^2.6.0",
69
69
  "gulp-connect": "^5.7.0",
70
- "gulp-eslint": "^4.0.0",
70
+ "gulp-eslint": "^6.0.0",
71
71
  "gulp-if": "^3.0.0",
72
72
  "gulp-js-escape": "^1.0.1",
73
73
  "gulp-replace": "^1.0.0",
@@ -122,7 +122,7 @@
122
122
  "criteo-direct-rsa-validate": "^1.1.0",
123
123
  "crypto-js": "^3.3.0",
124
124
  "dlv": "1.1.3",
125
- "dset": "2.0.1",
125
+ "dset": "3.1.2",
126
126
  "express": "^4.15.4",
127
127
  "fun-hooks": "^0.9.9",
128
128
  "just-clone": "^1.0.2",
@@ -17,22 +17,24 @@ import {
17
17
  logError,
18
18
  logInfo,
19
19
  logMessage,
20
- logWarn, mergeDeep,
20
+ logWarn,
21
+ mergeDeep,
21
22
  shuffle,
22
23
  timestamp,
23
24
  } from './utils.js';
24
25
  import {processAdUnitsForLabels} from './sizeMapping.js';
25
- import { decorateAdUnitsWithNativeParams, nativeAdapters } from './native.js';
26
- import { newBidder } from './adapters/bidderFactory.js';
27
- import { ajaxBuilder } from './ajax.js';
28
- import { config, RANDOM } from './config.js';
29
- import { hook } from './hook.js';
30
- import {includes, find} from './polyfill.js';
31
- import { adunitCounter } from './adUnits.js';
32
- import { getRefererInfo } from './refererDetection.js';
26
+ import {decorateAdUnitsWithNativeParams, nativeAdapters} from './native.js';
27
+ import {newBidder} from './adapters/bidderFactory.js';
28
+ import {ajaxBuilder} from './ajax.js';
29
+ import {config, RANDOM} from './config.js';
30
+ import {hook} from './hook.js';
31
+ import {find, includes} from './polyfill.js';
32
+ import {adunitCounter} from './adUnits.js';
33
+ import {getRefererInfo} from './refererDetection.js';
33
34
  import {GdprConsentHandler, UspConsentHandler} from './consentHandler.js';
34
35
  import * as events from './events.js';
35
36
  import CONSTANTS from './constants.json';
37
+ import {useMetrics} from './utils/perfMetrics.js';
36
38
 
37
39
  export const PARTITIONS = {
38
40
  CLIENT: 'client',
@@ -60,7 +62,7 @@ var _analyticsRegistry = {};
60
62
  * @property {Array<string>} activeLabels the labels specified as being active by requestBids
61
63
  */
62
64
 
63
- function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src}) {
65
+ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src, metrics}) {
64
66
  return adUnits.reduce((result, adUnit) => {
65
67
  result.push(adUnit.bids.filter(bid => bid.bidder === bidderCode)
66
68
  .reduce((bids, bid) => {
@@ -92,6 +94,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src}) {
92
94
  bidderRequestId,
93
95
  auctionId,
94
96
  src,
97
+ metrics,
95
98
  bidRequestsCount: adunitCounter.getRequestsCounter(adUnit.code),
96
99
  bidderRequestsCount: adunitCounter.getBidderRequestsCounter(adUnit.code, bid.bidder),
97
100
  bidderWinsCount: adunitCounter.getBidderWinsCounter(adUnit.code, bid.bidder),
@@ -206,7 +209,8 @@ export function _partitionBidders (adUnits, s2sConfigs, {getS2SBidders = getS2SB
206
209
 
207
210
  export const partitionBidders = hook('sync', _partitionBidders, 'partitionBidders');
208
211
 
209
- adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, auctionId, cbTimeout, labels, ortb2Fragments = {}) {
212
+ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, auctionId, cbTimeout, labels, ortb2Fragments = {}, auctionMetrics) {
213
+ auctionMetrics = useMetrics(auctionMetrics);
210
214
  /**
211
215
  * emit and pass adunits for external modification
212
216
  * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue}
@@ -244,16 +248,18 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a
244
248
  let uniquePbsTid = generateUUID();
245
249
  serverBidders.forEach(bidderCode => {
246
250
  const bidderRequestId = getUniqueIdentifierStr();
251
+ const metrics = auctionMetrics.fork();
247
252
  const bidderRequest = addOrtb2({
248
253
  bidderCode,
249
254
  auctionId,
250
255
  bidderRequestId,
251
256
  uniquePbsTid,
252
- bids: hookedGetBids({bidderCode, auctionId, bidderRequestId, 'adUnits': deepClone(adUnitsS2SCopy), src: CONSTANTS.S2S.SRC}),
257
+ bids: hookedGetBids({bidderCode, auctionId, bidderRequestId, 'adUnits': deepClone(adUnitsS2SCopy), src: CONSTANTS.S2S.SRC, metrics}),
253
258
  auctionStart: auctionStart,
254
259
  timeout: s2sConfig.timeout,
255
260
  src: CONSTANTS.S2S.SRC,
256
261
  refererInfo,
262
+ metrics,
257
263
  });
258
264
  if (bidderRequest.bids.length !== 0) {
259
265
  bidRequests.push(bidderRequest);
@@ -281,14 +287,16 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a
281
287
  let adUnitsClientCopy = getAdUnitCopyForClientAdapters(adUnits);
282
288
  clientBidders.forEach(bidderCode => {
283
289
  const bidderRequestId = getUniqueIdentifierStr();
290
+ const metrics = auctionMetrics.fork();
284
291
  const bidderRequest = addOrtb2({
285
292
  bidderCode,
286
293
  auctionId,
287
294
  bidderRequestId,
288
- bids: hookedGetBids({bidderCode, auctionId, bidderRequestId, 'adUnits': deepClone(adUnitsClientCopy), labels, src: 'client'}),
295
+ bids: hookedGetBids({bidderCode, auctionId, bidderRequestId, 'adUnits': deepClone(adUnitsClientCopy), labels, src: 'client', metrics}),
289
296
  auctionStart: auctionStart,
290
297
  timeout: cbTimeout,
291
298
  refererInfo,
299
+ metrics,
292
300
  });
293
301
  const adapter = _bidderRegistry[bidderCode];
294
302
  if (!adapter) {
@@ -15,6 +15,7 @@ import { getHook, hook } from '../hook.js';
15
15
  import { getCoreStorageManager } from '../storageManager.js';
16
16
  import {auctionManager} from '../auctionManager.js';
17
17
  import { bidderSettings } from '../bidderSettings.js';
18
+ import {useMetrics} from '../utils/perfMetrics.js';
18
19
 
19
20
  export const storage = getCoreStorageManager('bidderFactory');
20
21
 
@@ -196,8 +197,10 @@ export function newBidder(spec) {
196
197
 
197
198
  const adUnitCodesHandled = {};
198
199
  function addBidWithCode(adUnitCode, bid) {
200
+ const metrics = useMetrics(bid.metrics);
201
+ metrics.checkpoint('addBidResponse');
199
202
  adUnitCodesHandled[adUnitCode] = true;
200
- if (isValid(adUnitCode, bid)) {
203
+ if (metrics.measureTime('addBidResponse.validate', () => isValid(adUnitCode, bid))) {
201
204
  addBidResponse(adUnitCode, bid);
202
205
  }
203
206
  }
@@ -213,7 +216,9 @@ export function newBidder(spec) {
213
216
  });
214
217
  }
215
218
 
216
- const validBidRequests = bidderRequest.bids.filter(filterAndWarn);
219
+ const validBidRequests = adapterMetrics(bidderRequest)
220
+ .measureTime('validate', () => bidderRequest.bids.filter(filterAndWarn));
221
+
217
222
  if (validBidRequests.length === 0) {
218
223
  afterAllResponses();
219
224
  return;
@@ -315,7 +320,11 @@ export function newBidder(spec) {
315
320
  * @param onCompletion {function()} invoked once when all bid requests have been processed
316
321
  */
317
322
  export const processBidderRequests = hook('sync', function (spec, bids, bidderRequest, ajax, wrapCallback, {onRequest, onResponse, onFledgeAuctionConfigs, onError, onBid, onCompletion}) {
318
- let requests = spec.buildRequests(bids, bidderRequest);
323
+ const metrics = adapterMetrics(bidderRequest);
324
+ onCompletion = metrics.startTiming('total').stopBefore(onCompletion);
325
+
326
+ let requests = metrics.measureTime('buildRequests', () => spec.buildRequests(bids, bidderRequest));
327
+
319
328
  if (!requests || requests.length === 0) {
320
329
  onCompletion();
321
330
  return;
@@ -327,10 +336,16 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe
327
336
  const requestDone = delayExecution(onCompletion, requests.length);
328
337
 
329
338
  requests.forEach((request) => {
339
+ const requestMetrics = metrics.fork();
340
+ function addBid(bid) {
341
+ if (bid != null) bid.metrics = requestMetrics.fork().renameWith();
342
+ onBid(bid);
343
+ }
330
344
  // If the server responds successfully, use the adapter code to unpack the Bids from it.
331
345
  // If the adapter code fails, no bids should be added. After all the bids have been added,
332
346
  // make sure to call the `requestDone` function so that we're one step closer to calling onCompletion().
333
347
  const onSuccess = wrapCallback(function(response, responseObj) {
348
+ networkDone();
334
349
  try {
335
350
  response = JSON.parse(response);
336
351
  } catch (e) { /* response might not be JSON... that's ok. */ }
@@ -343,7 +358,7 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe
343
358
  onResponse(response);
344
359
 
345
360
  try {
346
- response = spec.interpretResponse(response, request);
361
+ response = requestMetrics.measureTime('interpretResponse', () => spec.interpretResponse(response, request));
347
362
  } catch (err) {
348
363
  logError(`Bidder ${spec.code} failed to interpret the server's response. Continuing without bids`, null, err);
349
364
  requestDone();
@@ -361,9 +376,9 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe
361
376
 
362
377
  if (bids) {
363
378
  if (isArray(bids)) {
364
- bids.forEach(onBid);
379
+ bids.forEach(addBid);
365
380
  } else {
366
- onBid(bids);
381
+ addBid(bids);
367
382
  }
368
383
  }
369
384
  requestDone();
@@ -376,11 +391,14 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe
376
391
  });
377
392
 
378
393
  const onFailure = wrapCallback(function (errorMessage, error) {
394
+ networkDone();
379
395
  onError(errorMessage, error);
380
396
  requestDone();
381
397
  });
382
398
 
383
399
  onRequest(request);
400
+
401
+ const networkDone = requestMetrics.startTiming('net');
384
402
  switch (request.method) {
385
403
  case 'GET':
386
404
  ajax(
@@ -588,3 +606,7 @@ export function isValid(adUnitCode, bid, {index = auctionManager.index} = {}) {
588
606
 
589
607
  return true;
590
608
  }
609
+
610
+ function adapterMetrics(bidderRequest) {
611
+ return useMetrics(bidderRequest.metrics).renameWith(n => [`adapter.client.${n}`, `adapters.client.${bidderRequest.bidderCode}.${n}`])
612
+ }