prebid.js 9.5.0 → 9.7.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 (253) hide show
  1. package/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  2. package/.github/release-drafter.yml +4 -0
  3. package/README.md +4 -4
  4. package/dist/33acrossAnalyticsAdapter.js +1 -1
  5. package/dist/33acrossBidAdapter.js +1 -1
  6. package/dist/33acrossIdSystem.js +1 -1
  7. package/dist/BTBidAdapter.js +1 -1
  8. package/dist/adagioAnalyticsAdapter.js +1 -1
  9. package/dist/adagioBidAdapter.js +1 -1
  10. package/dist/adagioUtils.js +1 -1
  11. package/dist/addefendBidAdapter.js +1 -1
  12. package/dist/adgenerationBidAdapter.js +1 -1
  13. package/dist/adkernelBidAdapter.js +1 -1
  14. package/dist/adlooxRtdProvider.js +1 -1
  15. package/dist/adqueryBidAdapter.js +1 -1
  16. package/dist/adrelevantisBidAdapter.js +1 -1
  17. package/dist/adstirBidAdapter.js +1 -1
  18. package/dist/adtrgtmeBidAdapter.js +1 -1
  19. package/dist/adxcgAnalyticsAdapter.js +1 -1
  20. package/dist/adxcgBidAdapter.js +1 -1
  21. package/dist/adyoulikeBidAdapter.js +1 -1
  22. package/dist/agmaAnalyticsAdapter.js +1 -1
  23. package/dist/aidemBidAdapter.js +1 -1
  24. package/dist/ajaBidAdapter.js +1 -1
  25. package/dist/amxBidAdapter.js +1 -1
  26. package/dist/amxIdSystem.js +1 -1
  27. package/dist/appierAnalyticsAdapter.js +1 -1
  28. package/dist/appnexusBidAdapter.js +1 -1
  29. package/dist/asoBidAdapter.js +1 -1
  30. package/dist/axonixBidAdapter.js +1 -1
  31. package/dist/bidglassBidAdapter.js +1 -1
  32. package/dist/big-richmediaBidAdapter.js +1 -1
  33. package/dist/bridBidAdapter.js +1 -1
  34. package/dist/bridgewellBidAdapter.js +1 -1
  35. package/dist/brightMountainMediaBidAdapter.js +1 -1
  36. package/dist/carodaBidAdapter.js +1 -1
  37. package/dist/chtnwBidAdapter.js +1 -1
  38. package/dist/chunk-core.js +1 -1
  39. package/dist/cleanmedianetBidAdapter.js +1 -1
  40. package/dist/concertBidAdapter.js +1 -1
  41. package/dist/connectIdSystem.js +1 -1
  42. package/dist/connectadBidAdapter.js +1 -1
  43. package/dist/connectionInfo.js +1 -0
  44. package/dist/consumableBidAdapter.js +1 -1
  45. package/dist/conversantAnalyticsAdapter.js +1 -1
  46. package/dist/conversantBidAdapter.js +1 -1
  47. package/dist/craftBidAdapter.js +1 -1
  48. package/dist/criteoBidAdapter.js +1 -1
  49. package/dist/cwireBidAdapter.js +1 -1
  50. package/dist/dailymotionBidAdapter.js +1 -1
  51. package/dist/dependencies.json +31 -2
  52. package/dist/docereeAdManagerBidAdapter.js +1 -1
  53. package/dist/dspxBidAdapter.js +1 -1
  54. package/dist/dxkultureBidAdapter.js +1 -1
  55. package/dist/eplanningBidAdapter.js +1 -1
  56. package/dist/euidIdSystem.js +1 -1
  57. package/dist/exadsBidAdapter.js +1 -1
  58. package/dist/feedadBidAdapter.js +1 -1
  59. package/dist/finativeBidAdapter.js +1 -1
  60. package/dist/freewheel-sspBidAdapter.js +1 -1
  61. package/dist/gamoshiBidAdapter.js +1 -1
  62. package/dist/gmosspBidAdapter.js +1 -1
  63. package/dist/goldbachBidAdapter.js +1 -1
  64. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  65. package/dist/greenbidsRtdProvider.js +1 -1
  66. package/dist/gridBidAdapter.js +1 -1
  67. package/dist/gumgumBidAdapter.js +1 -1
  68. package/dist/h12mediaBidAdapter.js +1 -1
  69. package/dist/hypelabBidAdapter.js +1 -1
  70. package/dist/id5AnalyticsAdapter.js +1 -1
  71. package/dist/id5IdSystem.js +1 -1
  72. package/dist/idImportLibrary.js +1 -1
  73. package/dist/imdsBidAdapter.js +1 -1
  74. package/dist/improvedigitalBidAdapter.js +1 -1
  75. package/dist/insticatorBidAdapter.js +1 -1
  76. package/dist/intentIqAnalyticsAdapter.js +1 -1
  77. package/dist/ixBidAdapter.js +1 -1
  78. package/dist/jixieBidAdapter.js +1 -1
  79. package/dist/justpremiumBidAdapter.js +1 -1
  80. package/dist/kargoBidAdapter.js +1 -1
  81. package/dist/kimberliteBidAdapter.js +1 -1
  82. package/dist/konduitAnalyticsAdapter.js +1 -1
  83. package/dist/kueezBidAdapter.js +1 -1
  84. package/dist/lassoBidAdapter.js +1 -1
  85. package/dist/lifestreetBidAdapter.js +1 -1
  86. package/dist/liveIntentIdSystem.js +1 -1
  87. package/dist/logicadBidAdapter.js +1 -1
  88. package/dist/loglyliftBidAdapter.js +1 -1
  89. package/dist/luceadBidAdapter.js +1 -1
  90. package/dist/mabidderBidAdapter.js +1 -1
  91. package/dist/magniteAnalyticsAdapter.js +1 -1
  92. package/dist/malltvAnalyticsAdapter.js +1 -1
  93. package/dist/marsmediaBidAdapter.js +1 -1
  94. package/dist/mediafuseBidAdapter.js +1 -1
  95. package/dist/medianetAnalyticsAdapter.js +1 -1
  96. package/dist/medianetBidAdapter.js +1 -1
  97. package/dist/mediasquareBidAdapter.js +1 -1
  98. package/dist/mgidBidAdapter.js +1 -1
  99. package/dist/minutemediaBidAdapter.js +1 -1
  100. package/dist/missenaBidAdapter.js +1 -1
  101. package/dist/mobianRtdProvider.js +1 -1
  102. package/dist/nexx360BidAdapter.js +1 -1
  103. package/dist/nobidAnalyticsAdapter.js +1 -1
  104. package/dist/nobidBidAdapter.js +1 -1
  105. package/dist/not-for-prod/prebid.js +174 -170
  106. package/dist/oguryBidAdapter.js +1 -1
  107. package/dist/onetagBidAdapter.js +1 -1
  108. package/dist/ooloAnalyticsAdapter.js +1 -1
  109. package/dist/openwebBidAdapter.js +1 -1
  110. package/dist/openxBidAdapter.js +1 -1
  111. package/dist/optidigitalBidAdapter.js +1 -1
  112. package/dist/orbidderBidAdapter.js +1 -1
  113. package/dist/ortbConverter.js +1 -1
  114. package/dist/outbrainBidAdapter.js +1 -1
  115. package/dist/ownadxBidAdapter.js +1 -0
  116. package/dist/pixfutureBidAdapter.js +1 -1
  117. package/dist/prismaBidAdapter.js +1 -1
  118. package/dist/publinkIdSystem.js +1 -1
  119. package/dist/publirBidAdapter.js +1 -1
  120. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  121. package/dist/pubmaticBidAdapter.js +1 -1
  122. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  123. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  124. package/dist/pxyzBidAdapter.js +1 -1
  125. package/dist/quantcastBidAdapter.js +1 -1
  126. package/dist/readpeakBidAdapter.js +1 -1
  127. package/dist/relaidoBidAdapter.js +1 -1
  128. package/dist/retailspotBidAdapter.js +1 -1
  129. package/dist/rhythmoneBidAdapter.js +1 -1
  130. package/dist/riseBidAdapter.js +1 -1
  131. package/dist/riseUtils.js +1 -0
  132. package/dist/rubiconBidAdapter.js +1 -1
  133. package/dist/seedingAllianceBidAdapter.js +1 -1
  134. package/dist/seedtagBidAdapter.js +1 -1
  135. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  136. package/dist/sharethroughBidAdapter.js +1 -1
  137. package/dist/shinezBidAdapter.js +1 -1
  138. package/dist/smaatoBidAdapter.js +1 -1
  139. package/dist/smartadserverBidAdapter.js +1 -1
  140. package/dist/smarthubBidAdapter.js +1 -1
  141. package/dist/smartxBidAdapter.js +1 -1
  142. package/dist/smilewantedBidAdapter.js +1 -1
  143. package/dist/snigelBidAdapter.js +1 -1
  144. package/dist/sonobiBidAdapter.js +1 -1
  145. package/dist/sovrnBidAdapter.js +1 -1
  146. package/dist/sspBCBidAdapter.js +1 -1
  147. package/dist/stnBidAdapter.js +1 -1
  148. package/dist/stvBidAdapter.js +1 -1
  149. package/dist/sublimeBidAdapter.js +1 -1
  150. package/dist/taboolaBidAdapter.js +1 -1
  151. package/dist/tappxBidAdapter.js +1 -1
  152. package/dist/targetVideoBidAdapter.js +1 -1
  153. package/dist/teadsBidAdapter.js +1 -1
  154. package/dist/telariaBidAdapter.js +1 -1
  155. package/dist/teqblazeUtils.js +1 -1
  156. package/dist/terceptAnalyticsAdapter.js +1 -1
  157. package/dist/themoneytizerBidAdapter.js +1 -1
  158. package/dist/trionBidAdapter.js +1 -1
  159. package/dist/tripleliftBidAdapter.js +1 -1
  160. package/dist/ttdBidAdapter.js +1 -1
  161. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  162. package/dist/uid2IdSystem.js +1 -1
  163. package/dist/uid2IdSystem_shared.js +1 -1
  164. package/dist/underdogmediaBidAdapter.js +1 -1
  165. package/dist/undertoneBidAdapter.js +1 -1
  166. package/dist/unrulyBidAdapter.js +1 -1
  167. package/dist/viantOrtbBidAdapter.js +1 -1
  168. package/dist/vidazooUtils.js +1 -1
  169. package/dist/videobyteBidAdapter.js +1 -1
  170. package/dist/visxBidAdapter.js +1 -1
  171. package/dist/vuukleBidAdapter.js +1 -1
  172. package/dist/widespaceBidAdapter.js +1 -1
  173. package/dist/winrBidAdapter.js +1 -1
  174. package/dist/wurflRtdProvider.js +1 -0
  175. package/dist/yahooAdsBidAdapter.js +1 -1
  176. package/dist/yieldmoBidAdapter.js +1 -1
  177. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  178. package/integrationExamples/gpt/wurflRtdProvider_example.html +106 -0
  179. package/libraries/connectionInfo/connectionUtils.js +33 -0
  180. package/libraries/ortbConverter/processors/video.js +2 -24
  181. package/libraries/riseUtils/index.js +331 -0
  182. package/libraries/teqblazeUtils/bidderUtils.js +15 -11
  183. package/libraries/vidazooUtils/bidderUtils.js +16 -0
  184. package/modules/.submodules.json +3 -1
  185. package/modules/51DegreesRtdProvider.md +11 -7
  186. package/modules/adagioAnalyticsAdapter.js +101 -84
  187. package/modules/adagioAnalyticsAdapter.md +4 -0
  188. package/modules/adagioBidAdapter.js +14 -66
  189. package/modules/adkernelBidAdapter.js +2 -2
  190. package/modules/aidemBidAdapter.js +2 -0
  191. package/modules/appnexusBidAdapter.js +1 -1
  192. package/modules/cleanmedianetBidAdapter.js +1 -1
  193. package/modules/connectIdSystem.js +5 -3
  194. package/modules/dailymotionBidAdapter.js +19 -0
  195. package/modules/docereeAdManagerBidAdapter.js +9 -12
  196. package/modules/gamoshiBidAdapter.js +1 -1
  197. package/modules/greenbidsAnalyticsAdapter.js +7 -4
  198. package/modules/greenbidsRtdProvider.js +15 -5
  199. package/modules/id5IdSystem.js +14 -93
  200. package/modules/idImportLibrary.js +14 -6
  201. package/modules/insticatorBidAdapter.js +35 -31
  202. package/modules/kargoBidAdapter.js +11 -14
  203. package/modules/kimberliteBidAdapter.js +24 -7
  204. package/modules/kimberliteBidAdapter.md +30 -1
  205. package/modules/minutemediaBidAdapter.js +21 -398
  206. package/modules/mobianRtdProvider.js +28 -14
  207. package/modules/nexx360BidAdapter.js +3 -2
  208. package/modules/openwebBidAdapter.js +18 -396
  209. package/modules/ownadxBidAdapter.js +99 -0
  210. package/modules/prismaBidAdapter.js +1 -27
  211. package/modules/publirBidAdapter.js +20 -273
  212. package/modules/pubmaticBidAdapter.js +2 -1
  213. package/modules/resetdigitalBidAdapter.md +54 -45
  214. package/modules/riseBidAdapter.js +19 -400
  215. package/modules/sharethroughBidAdapter.js +5 -0
  216. package/modules/shinezBidAdapter.js +17 -361
  217. package/modules/smarthubBidAdapter.js +45 -177
  218. package/modules/stnBidAdapter.js +22 -398
  219. package/modules/telariaBidAdapter.js +2 -32
  220. package/modules/ttdBidAdapter.js +14 -32
  221. package/modules/uid2IdSystem_shared.js +51 -39
  222. package/modules/viantOrtbBidAdapter.js +1 -1
  223. package/modules/wurflRtdProvider.js +213 -0
  224. package/modules/wurflRtdProvider.md +67 -0
  225. package/package.json +1 -1
  226. package/src/adloader.js +1 -0
  227. package/src/prebid.js +6 -1
  228. package/src/video.js +78 -1
  229. package/test/spec/modules/adagioAnalyticsAdapter_spec.js +231 -141
  230. package/test/spec/modules/adagioBidAdapter_spec.js +32 -67
  231. package/test/spec/modules/appnexusBidAdapter_spec.js +0 -33
  232. package/test/spec/modules/cleanmedianetBidAdapter_spec.js +1 -1
  233. package/test/spec/modules/connectIdSystem_spec.js +27 -28
  234. package/test/spec/modules/dailymotionBidAdapter_spec.js +50 -0
  235. package/test/spec/modules/docereeAdManagerBidAdapter_spec.js +1 -0
  236. package/test/spec/modules/gamoshiBidAdapter_spec.js +1 -1
  237. package/test/spec/modules/id5IdSystem_spec.js +25 -98
  238. package/test/spec/modules/idImportLibrary_spec.js +14 -0
  239. package/test/spec/modules/insticatorBidAdapter_spec.js +8 -1
  240. package/test/spec/modules/kargoBidAdapter_spec.js +7 -8
  241. package/test/spec/modules/kimberliteBidAdapter_spec.js +137 -65
  242. package/test/spec/modules/mobianRtdProvider_spec.js +46 -43
  243. package/test/spec/modules/nexx360BidAdapter_spec.js +1 -1
  244. package/test/spec/modules/ownadxBidAdapter_spec.js +103 -0
  245. package/test/spec/modules/pubmaticBidAdapter_spec.js +18 -0
  246. package/test/spec/modules/sharethroughBidAdapter_spec.js +35 -0
  247. package/test/spec/modules/smarthubBidAdapter_spec.js +6 -4
  248. package/test/spec/modules/ttdBidAdapter_spec.js +10 -1
  249. package/test/spec/modules/viantOrtbBidAdapter_spec.js +1 -1
  250. package/test/spec/modules/vidazooBidAdapter_spec.js +12 -4
  251. package/test/spec/modules/wurflRtdProvider_spec.js +324 -0
  252. package/test/spec/unit/pbjs_api_spec.js +11 -0
  253. package/test/spec/video_spec.js +103 -1
@@ -7,12 +7,22 @@ function isValidIdentity(identity) {
7
7
  return !!(typeof identity === 'object' && identity !== null && identity.advertising_token && identity.identity_expires && identity.refresh_from && identity.refresh_token && identity.refresh_expires);
8
8
  }
9
9
 
10
+ // Helper function to prepend message
11
+ function prependMessage(message) {
12
+ return `UID2 shared library - ${message}`;
13
+ }
14
+
15
+ // Wrapper function for logInfo
16
+ function logInfoWrapper(logInfo, ...args) {
17
+ logInfo(prependMessage(args[0]), ...args.slice(1));
18
+ }
19
+
10
20
  // This is extracted from an in-progress API client. Once it's available via NPM, this class should be replaced with the NPM package.
11
21
  export class Uid2ApiClient {
12
22
  constructor(opts, clientId, logInfo, logWarn) {
13
23
  this._baseUrl = opts.baseUrl;
14
24
  this._clientVersion = clientId;
15
- this._logInfo = logInfo;
25
+ this._logInfo = (...args) => logInfoWrapper(logInfo, ...args);
16
26
  this._logWarn = logWarn;
17
27
  }
18
28
 
@@ -35,7 +45,7 @@ export class Uid2ApiClient {
35
45
  if (this.isValidRefreshResponse(response)) {
36
46
  if (response.status === 'success') { return { status: response.status, identity: response.body }; }
37
47
  return response;
38
- } else { return `Response didn't contain a valid status`; }
48
+ } else { return prependMessage(`Response didn't contain a valid status`); }
39
49
  }
40
50
  callRefreshApi(refreshDetails) {
41
51
  const url = this._baseUrl + '/v2/token/refresh';
@@ -53,7 +63,7 @@ export class Uid2ApiClient {
53
63
  this._logInfo('No response decryption key available, assuming unencrypted JSON');
54
64
  const response = JSON.parse(responseText);
55
65
  const result = this.ResponseToRefreshResult(response);
56
- if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); }
66
+ if (typeof result === 'string') { rejectPromise(prependMessage(result)); } else { resolvePromise(result); }
57
67
  } else {
58
68
  this._logInfo('Decrypting refresh API response');
59
69
  const encodeResp = this.createArrayBuffer(atob(responseText));
@@ -69,12 +79,12 @@ export class Uid2ApiClient {
69
79
  this._logInfo('Decrypted to:', decryptedResponse);
70
80
  const response = JSON.parse(decryptedResponse);
71
81
  const result = this.ResponseToRefreshResult(response);
72
- if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); }
73
- }, (reason) => this._logWarn(`Call to UID2 API failed`, reason));
74
- }, (reason) => this._logWarn(`Call to UID2 API failed`, reason));
82
+ if (typeof result === 'string') { rejectPromise(prependMessage(result)); } else { resolvePromise(result); }
83
+ }, (reason) => this._logWarn(prependMessage(`Call to UID2 API failed`), reason));
84
+ }, (reason) => this._logWarn(prependMessage(`Call to UID2 API failed`), reason));
75
85
  }
76
86
  } catch (_err) {
77
- rejectPromise(responseText);
87
+ rejectPromise(prependMessage(responseText));
78
88
  }
79
89
  },
80
90
  error: (error, xhr) => {
@@ -82,9 +92,9 @@ export class Uid2ApiClient {
82
92
  this._logInfo('Error status, assuming unencrypted JSON');
83
93
  const response = JSON.parse(xhr.responseText);
84
94
  const result = this.ResponseToRefreshResult(response);
85
- if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); }
95
+ if (typeof result === 'string') { rejectPromise(prependMessage(result)); } else { resolvePromise(result); }
86
96
  } catch (_e) {
87
- rejectPromise(error)
97
+ rejectPromise(prependMessage(error));
88
98
  }
89
99
  }
90
100
  }, refreshDetails.refresh_token, { method: 'POST',
@@ -99,7 +109,7 @@ export class Uid2StorageManager {
99
109
  this._storage = storage;
100
110
  this._preferLocalStorage = preferLocalStorage;
101
111
  this._storageName = storageName;
102
- this._logInfo = logInfo;
112
+ this._logInfo = (...args) => logInfoWrapper(logInfo, ...args);
103
113
  }
104
114
  readCookie(cookieName) {
105
115
  return this._storage.cookiesAreEnabled() ? this._storage.getCookie(cookieName) : null;
@@ -392,7 +402,7 @@ if (FEATURES.UID2_CSTG) {
392
402
  this._baseUrl = opts.baseUrl;
393
403
  this._serverPublicKey = opts.cstg.serverPublicKey;
394
404
  this._subscriptionId = opts.cstg.subscriptionId;
395
- this._logInfo = logInfo;
405
+ this._logInfo = (...args) => logInfoWrapper(logInfo, ...args);
396
406
  this._logWarn = logWarn;
397
407
  }
398
408
 
@@ -512,11 +522,11 @@ if (FEATURES.UID2_CSTG) {
512
522
  // A 200 should always be a success response.
513
523
  // Something has gone wrong.
514
524
  rejectPromise(
515
- `API error: Response body was invalid for HTTP status 200: ${decryptedResponse}`
525
+ prependMessage(`API error: Response body was invalid for HTTP status 200: ${decryptedResponse}`)
516
526
  );
517
527
  }
518
528
  } catch (err) {
519
- rejectPromise(err);
529
+ rejectPromise(prependMessage(err));
520
530
  }
521
531
  },
522
532
  error: (error, xhr) => {
@@ -524,32 +534,32 @@ if (FEATURES.UID2_CSTG) {
524
534
  if (xhr.status === 400) {
525
535
  const response = JSON.parse(xhr.responseText);
526
536
  if (this.isCstgApiClientErrorResponse(response)) {
527
- rejectPromise(`Client error: ${response.message}`);
537
+ rejectPromise(prependMessage(`Client error: ${response.message}`));
528
538
  } else {
529
539
  // A 400 should always be a client error.
530
540
  // Something has gone wrong.
531
541
  rejectPromise(
532
- `API error: Response body was invalid for HTTP status 400: ${xhr.responseText}`
542
+ prependMessage(`UID2 API error: Response body was invalid for HTTP status 400: ${xhr.responseText}`)
533
543
  );
534
544
  }
535
545
  } else if (xhr.status === 403) {
536
546
  const response = JSON.parse(xhr.responseText);
537
547
  if (this.isCstgApiForbiddenResponse(xhr)) {
538
- rejectPromise(`Forbidden: ${response.message}`);
548
+ rejectPromise(prependMessage(`Forbidden: ${response.message}`));
539
549
  } else {
540
550
  // A 403 should always be a forbidden response.
541
551
  // Something has gone wrong.
542
552
  rejectPromise(
543
- `API error: Response body was invalid for HTTP status 403: ${xhr.responseText}`
553
+ prependMessage(`UID2 API error: Response body was invalid for HTTP status 403: ${xhr.responseText}`)
544
554
  );
545
555
  }
546
556
  } else {
547
557
  rejectPromise(
548
- `API error: Unexpected HTTP status ${xhr.status}: ${error}`
558
+ prependMessage(`UID2 API error: Unexpected HTTP status ${xhr.status}: ${error}`)
549
559
  );
550
560
  }
551
561
  } catch (_e) {
552
- rejectPromise(error);
562
+ rejectPromise(prependMessage(error));
553
563
  }
554
564
  },
555
565
  },
@@ -678,43 +688,45 @@ if (FEATURES.UID2_CSTG) {
678
688
  }
679
689
 
680
690
  export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) {
691
+ const logInfo = (...args) => logInfoWrapper(_logInfo, ...args);
692
+
681
693
  let suppliedToken = null;
682
694
  const preferLocalStorage = (config.storage !== 'cookie');
683
- const storageManager = new Uid2StorageManager(prebidStorageManager, preferLocalStorage, config.internalStorage, _logInfo);
684
- _logInfo(`Module is using ${preferLocalStorage ? 'local storage' : 'cookies'} for internal storage.`);
695
+ const storageManager = new Uid2StorageManager(prebidStorageManager, preferLocalStorage, config.internalStorage, logInfo);
696
+ logInfo(`Module is using ${preferLocalStorage ? 'local storage' : 'cookies'} for internal storage.`);
685
697
 
686
698
  const isCstgEnabled =
687
699
  clientSideTokenGenerator &&
688
700
  clientSideTokenGenerator.isCSTGOptionsValid(config.cstg, _logWarn);
689
701
  if (isCstgEnabled) {
690
- _logInfo(`Module is using client-side token generation.`);
702
+ logInfo(`Module is using client-side token generation.`);
691
703
  // Ignores config.paramToken and config.serverCookieName if any is provided
692
704
  suppliedToken = null;
693
705
  } else if (config.paramToken) {
694
706
  suppliedToken = config.paramToken;
695
- _logInfo('Read token from params', suppliedToken);
707
+ logInfo('Read token from params', suppliedToken);
696
708
  } else if (config.serverCookieName) {
697
709
  suppliedToken = storageManager.readProvidedCookie(config.serverCookieName);
698
- _logInfo('Read token from server-supplied cookie', suppliedToken);
710
+ logInfo('Read token from server-supplied cookie', suppliedToken);
699
711
  }
700
712
  let storedTokens = storageManager.getStoredValueWithFallback();
701
- _logInfo('Loaded module-stored tokens:', storedTokens);
713
+ logInfo('Loaded module-stored tokens:', storedTokens);
702
714
 
703
715
  if (storedTokens && typeof storedTokens === 'string') {
704
716
  // Stored value is a plain token - if no token is supplied, just use the stored value.
705
717
 
706
718
  if (!suppliedToken && !isCstgEnabled) {
707
- _logInfo('Returning legacy cookie value.');
719
+ logInfo('Returning legacy cookie value.');
708
720
  return { id: storedTokens };
709
721
  }
710
722
  // Otherwise, ignore the legacy value - it should get over-written later anyway.
711
- _logInfo('Discarding superseded legacy cookie.');
723
+ logInfo('Discarding superseded legacy cookie.');
712
724
  storedTokens = null;
713
725
  }
714
726
 
715
727
  if (suppliedToken && storedTokens) {
716
728
  if (storedTokens.originalToken?.advertising_token !== suppliedToken.advertising_token) {
717
- _logInfo('Server supplied new token - ignoring stored value.', storedTokens.originalToken?.advertising_token, suppliedToken.advertising_token);
729
+ logInfo('Server supplied new token - ignoring stored value.', storedTokens.originalToken?.advertising_token, suppliedToken.advertising_token);
718
730
  // Stored token wasn't originally sourced from the provided token - ignore the stored value. A new user has logged in?
719
731
  storedTokens = null;
720
732
  }
@@ -723,16 +735,16 @@ export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) {
723
735
  if (FEATURES.UID2_CSTG && isCstgEnabled) {
724
736
  const cstgIdentity = clientSideTokenGenerator.getValidIdentity(config.cstg, _logWarn);
725
737
  if (cstgIdentity) {
726
- if (storedTokens && clientSideTokenGenerator.isStoredTokenInvalid(cstgIdentity, storedTokens, _logInfo, _logWarn)) {
738
+ if (storedTokens && clientSideTokenGenerator.isStoredTokenInvalid(cstgIdentity, storedTokens, logInfo, _logWarn)) {
727
739
  storedTokens = null;
728
740
  }
729
741
 
730
742
  if (!storedTokens || Date.now() > storedTokens.latestToken.refresh_expires) {
731
- const promise = clientSideTokenGenerator.generateTokenAndStore(config.apiBaseUrl, config.cstg, cstgIdentity, storageManager, _logInfo, _logWarn);
732
- _logInfo('Generate token using CSTG');
743
+ const promise = clientSideTokenGenerator.generateTokenAndStore(config.apiBaseUrl, config.cstg, cstgIdentity, storageManager, logInfo, _logWarn);
744
+ logInfo('Generate token using CSTG');
733
745
  return { callback: (cb) => {
734
746
  promise.then((result) => {
735
- _logInfo('Token generation responded, passing the new token on.', result);
747
+ logInfo('Token generation responded, passing the new token on.', result);
736
748
  cb(result);
737
749
  });
738
750
  } };
@@ -742,25 +754,25 @@ export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) {
742
754
 
743
755
  const useSuppliedToken = !(storedTokens?.latestToken) || (suppliedToken && suppliedToken.identity_expires > storedTokens.latestToken.identity_expires);
744
756
  const newestAvailableToken = useSuppliedToken ? suppliedToken : storedTokens.latestToken;
745
- _logInfo('UID2 module selected latest token', useSuppliedToken, newestAvailableToken);
757
+ logInfo('UID2 module selected latest token', useSuppliedToken, newestAvailableToken);
746
758
  if ((!newestAvailableToken || Date.now() > newestAvailableToken.refresh_expires)) {
747
- _logInfo('Newest available token is expired and not refreshable.');
759
+ logInfo('Newest available token is expired and not refreshable.');
748
760
  return { id: null };
749
761
  }
750
762
  if (Date.now() > newestAvailableToken.identity_expires) {
751
- const promise = refreshTokenAndStore(config.apiBaseUrl, newestAvailableToken, config.clientId, storageManager, _logInfo, _logWarn);
752
- _logInfo('Token is expired but can be refreshed, attempting refresh.');
763
+ const promise = refreshTokenAndStore(config.apiBaseUrl, newestAvailableToken, config.clientId, storageManager, logInfo, _logWarn);
764
+ logInfo('Token is expired but can be refreshed, attempting refresh.');
753
765
  return { callback: (cb) => {
754
766
  promise.then((result) => {
755
- _logInfo('Refresh reponded, passing the updated token on.', result);
767
+ logInfo('Refresh reponded, passing the updated token on.', result);
756
768
  cb(result);
757
769
  });
758
770
  } };
759
771
  }
760
772
  // If should refresh (but don't need to), refresh in the background.
761
773
  if (Date.now() > newestAvailableToken.refresh_from) {
762
- _logInfo(`Refreshing token in background with low priority.`);
763
- refreshTokenAndStore(config.apiBaseUrl, newestAvailableToken, config.clientId, storageManager, _logInfo, _logWarn);
774
+ logInfo(`Refreshing token in background with low priority.`);
775
+ refreshTokenAndStore(config.apiBaseUrl, newestAvailableToken, config.clientId, storageManager, logInfo, _logWarn);
764
776
  }
765
777
  const tokens = {
766
778
  originalToken: suppliedToken ?? storedTokens?.originalToken,
@@ -5,7 +5,7 @@ import {ortbConverter} from '../libraries/ortbConverter/converter.js'
5
5
  import {deepAccess, getBidIdParameter, logError} from '../src/utils.js';
6
6
 
7
7
  const BIDDER_CODE = 'viant';
8
- const ENDPOINT = 'https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder'
8
+ const ENDPOINT = 'https://bidders-us.adelphic.net/d/rtb/v25/prebid/bidder'
9
9
  const ADAPTER_VERSION = '2.0.0';
10
10
 
11
11
  const DEFAULT_BID_TTL = 300;
@@ -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.5.0",
3
+ "version": "9.7.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
 
@@ -869,6 +870,10 @@ pbjsInstance.getHighestCpmBids = function (adUnitCode) {
869
870
  return targeting.getWinningBids(adUnitCode);
870
871
  };
871
872
 
873
+ pbjsInstance.clearAllAuctions = function () {
874
+ auctionManager.clearAllAuctions();
875
+ };
876
+
872
877
  if (FEATURES.VIDEO) {
873
878
  /**
874
879
  * Mark the winning bid as used, should only be used in conjunction with video
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