prebid.js 9.53.3 → 9.53.4

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 (208) hide show
  1. package/dist/33acrossAnalyticsAdapter.js +1 -1
  2. package/dist/33acrossBidAdapter.js +1 -1
  3. package/dist/33acrossIdSystem.js +1 -1
  4. package/dist/BTBidAdapter.js +1 -1
  5. package/dist/adagioAnalyticsAdapter.js +1 -1
  6. package/dist/adagioBidAdapter.js +1 -1
  7. package/dist/adagioUtils.js +1 -1
  8. package/dist/addefendBidAdapter.js +1 -1
  9. package/dist/adgenerationBidAdapter.js +1 -1
  10. package/dist/adlooxRtdProvider.js +1 -1
  11. package/dist/adqueryBidAdapter.js +1 -1
  12. package/dist/adrelevantisBidAdapter.js +1 -1
  13. package/dist/adstirBidAdapter.js +1 -1
  14. package/dist/adtrgtmeBidAdapter.js +1 -1
  15. package/dist/adxcgAnalyticsAdapter.js +1 -1
  16. package/dist/adxcgBidAdapter.js +1 -1
  17. package/dist/adyoulikeBidAdapter.js +1 -1
  18. package/dist/agmaAnalyticsAdapter.js +1 -1
  19. package/dist/ajaBidAdapter.js +1 -1
  20. package/dist/amxBidAdapter.js +1 -1
  21. package/dist/amxIdSystem.js +1 -1
  22. package/dist/aniviewBidAdapter.js +1 -1
  23. package/dist/appierAnalyticsAdapter.js +1 -1
  24. package/dist/appnexusBidAdapter.js +1 -1
  25. package/dist/asoBidAdapter.js +1 -1
  26. package/dist/axonixBidAdapter.js +1 -1
  27. package/dist/beopBidAdapter.js +1 -1
  28. package/dist/bidderTimeoutUtils.js +1 -0
  29. package/dist/bidglassBidAdapter.js +1 -1
  30. package/dist/big-richmediaBidAdapter.js +1 -1
  31. package/dist/bitmediaBidAdapter.js +1 -1
  32. package/dist/bridBidAdapter.js +1 -1
  33. package/dist/bridgeuppBidAdapter.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/concertBidAdapter.js +1 -1
  40. package/dist/connectadBidAdapter.js +1 -1
  41. package/dist/consumableBidAdapter.js +1 -1
  42. package/dist/contxtfulBidAdapter.js +1 -1
  43. package/dist/conversantAnalyticsAdapter.js +1 -1
  44. package/dist/conversantBidAdapter.js +1 -1
  45. package/dist/craftBidAdapter.js +1 -1
  46. package/dist/criteoBidAdapter.js +1 -1
  47. package/dist/cwireBidAdapter.js +1 -1
  48. package/dist/dailymotionBidAdapter.js +1 -1
  49. package/dist/dependencies.json +10 -1
  50. package/dist/dspxBidAdapter.js +1 -1
  51. package/dist/dxkultureBidAdapter.js +1 -1
  52. package/dist/eplanningBidAdapter.js +1 -1
  53. package/dist/equativBidAdapter.js +1 -1
  54. package/dist/eskimiBidAdapter.js +1 -1
  55. package/dist/euidIdSystem.js +1 -1
  56. package/dist/exadsBidAdapter.js +1 -1
  57. package/dist/excoBidAdapter.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/fwsspBidAdapter.js +1 -1
  62. package/dist/gmosspBidAdapter.js +1 -1
  63. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  64. package/dist/greenbidsBidAdapter.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/imdsBidAdapter.js +1 -1
  73. package/dist/improvedigitalBidAdapter.js +1 -1
  74. package/dist/inmobiBidAdapter.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/liveIntentId.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/madsenseBidAdapter.js +1 -1
  92. package/dist/magniteAnalyticsAdapter.js +1 -1
  93. package/dist/malltvAnalyticsAdapter.js +1 -1
  94. package/dist/marsmediaBidAdapter.js +1 -1
  95. package/dist/mediafuseBidAdapter.js +1 -1
  96. package/dist/medianetBidAdapter.js +1 -1
  97. package/dist/medianetUtils.js +1 -1
  98. package/dist/mediasquareBidAdapter.js +1 -1
  99. package/dist/mgidBidAdapter.js +1 -1
  100. package/dist/missenaBidAdapter.js +1 -1
  101. package/dist/mobilefuseBidAdapter.js +1 -1
  102. package/dist/nextMillenniumBidAdapter.js +1 -1
  103. package/dist/nexx360Utils.js +1 -1
  104. package/dist/nobidAnalyticsAdapter.js +1 -1
  105. package/dist/nobidBidAdapter.js +1 -1
  106. package/dist/nodalsAiRtdProvider.js +1 -1
  107. package/dist/not-for-prod/prebid.js +175 -172
  108. package/dist/objectGuard.js +1 -1
  109. package/dist/oguryBidAdapter.js +1 -1
  110. package/dist/onetagBidAdapter.js +1 -1
  111. package/dist/ooloAnalyticsAdapter.js +1 -1
  112. package/dist/openxBidAdapter.js +1 -1
  113. package/dist/optableRtdProvider.js +1 -1
  114. package/dist/optidigitalBidAdapter.js +1 -1
  115. package/dist/orbidderBidAdapter.js +1 -1
  116. package/dist/outbrainBidAdapter.js +1 -1
  117. package/dist/pixfutureBidAdapter.js +1 -1
  118. package/dist/publinkIdSystem.js +1 -1
  119. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  120. package/dist/pubmaticBidAdapter.js +1 -1
  121. package/dist/pubmaticRtdProvider.js +1 -1
  122. package/dist/pubmaticUtils.js +1 -0
  123. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  124. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  125. package/dist/pxyzBidAdapter.js +1 -1
  126. package/dist/quantcastBidAdapter.js +1 -1
  127. package/dist/readpeakBidAdapter.js +1 -1
  128. package/dist/relaidoBidAdapter.js +1 -1
  129. package/dist/retailspotBidAdapter.js +1 -1
  130. package/dist/rhythmoneBidAdapter.js +1 -1
  131. package/dist/riseUtils.js +1 -1
  132. package/dist/rtdModule.js +1 -1
  133. package/dist/rubiconBidAdapter.js +1 -1
  134. package/dist/seedingAllianceBidAdapter.js +1 -1
  135. package/dist/seedtagBidAdapter.js +1 -1
  136. package/dist/sevioBidAdapter.js +1 -0
  137. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  138. package/dist/sharethroughBidAdapter.js +1 -1
  139. package/dist/showheroes-bsBidAdapter.js +1 -1
  140. package/dist/smaatoBidAdapter.js +1 -1
  141. package/dist/smartadserverBidAdapter.js +1 -1
  142. package/dist/smartxBidAdapter.js +1 -1
  143. package/dist/smilewantedBidAdapter.js +1 -1
  144. package/dist/snigelBidAdapter.js +1 -1
  145. package/dist/sonobiBidAdapter.js +1 -1
  146. package/dist/sovrnBidAdapter.js +1 -1
  147. package/dist/sparteoBidAdapter.js +1 -1
  148. package/dist/sspBCBidAdapter.js +1 -1
  149. package/dist/stvBidAdapter.js +1 -1
  150. package/dist/sublimeBidAdapter.js +1 -1
  151. package/dist/taboolaBidAdapter.js +1 -1
  152. package/dist/tappxBidAdapter.js +1 -1
  153. package/dist/targetVideoBidAdapter.js +1 -1
  154. package/dist/teadsBidAdapter.js +1 -1
  155. package/dist/terceptAnalyticsAdapter.js +1 -1
  156. package/dist/themoneytizerBidAdapter.js +1 -1
  157. package/dist/timeoutRtdProvider.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/underdogmediaBidAdapter.js +1 -1
  164. package/dist/undertoneBidAdapter.js +1 -1
  165. package/dist/unrulyBidAdapter.js +1 -1
  166. package/dist/vidazooUtils.js +1 -1
  167. package/dist/videobyteBidAdapter.js +1 -1
  168. package/dist/visxBidAdapter.js +1 -1
  169. package/dist/vuukleBidAdapter.js +1 -1
  170. package/dist/widespaceBidAdapter.js +1 -1
  171. package/dist/winrBidAdapter.js +1 -1
  172. package/dist/yahooAdsBidAdapter.js +1 -1
  173. package/dist/yandexBidAdapter.js +1 -1
  174. package/dist/yieldmoBidAdapter.js +1 -1
  175. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  176. package/integrationExamples/gpt/pubmaticRtdProvider_Example.html +161 -0
  177. package/libraries/bidderTimeoutUtils/bidderTimeoutUtils.js +119 -0
  178. package/libraries/objectGuard/objectGuard.js +36 -15
  179. package/libraries/pubmaticUtils/plugins/dynamicTimeout.js +209 -0
  180. package/libraries/pubmaticUtils/plugins/floorProvider.js +168 -0
  181. package/libraries/pubmaticUtils/plugins/pluginManager.js +106 -0
  182. package/libraries/pubmaticUtils/plugins/unifiedPricingRule.js +375 -0
  183. package/libraries/pubmaticUtils/pubmaticUtils.js +76 -0
  184. package/modules/fwsspBidAdapter.js +134 -69
  185. package/modules/fwsspBidAdapter.md +121 -26
  186. package/modules/optableRtdProvider.js +33 -12
  187. package/modules/pubmaticAnalyticsAdapter.js +5 -1
  188. package/modules/pubmaticRtdProvider.js +105 -565
  189. package/modules/rtdModule/index.js +23 -2
  190. package/modules/sevioBidAdapter.js +413 -0
  191. package/modules/sevioBidAdapter.md +29 -0
  192. package/modules/sparteoBidAdapter.js +122 -10
  193. package/modules/timeoutRtdProvider.js +2 -105
  194. package/package.json +1 -1
  195. package/test/spec/activities/objectGuard_spec.js +49 -16
  196. package/test/spec/libraries/bidderTimeoutUtils/bidderTimeoutUtils_spec.js +213 -0
  197. package/test/spec/libraries/pubmaticUtils/plugins/dynamicTimeout_spec.js +746 -0
  198. package/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js +184 -0
  199. package/test/spec/libraries/pubmaticUtils/plugins/pluginManager_spec.js +489 -0
  200. package/test/spec/libraries/pubmaticUtils/plugins/unifiedPricingRule_spec.js +359 -0
  201. package/test/spec/libraries/pubmaticUtils/pubmaticUtils_spec.js +236 -0
  202. package/test/spec/modules/fwsspBidAdapter_spec.js +513 -78
  203. package/test/spec/modules/optableRtdProvider_spec.js +55 -5
  204. package/test/spec/modules/pubmaticRtdProvider_spec.js +252 -1183
  205. package/test/spec/modules/realTimeDataModule_spec.js +58 -8
  206. package/test/spec/modules/sevioBidAdapter_spec.js +513 -0
  207. package/test/spec/modules/sparteoBidAdapter_spec.js +528 -43
  208. package/test/spec/modules/timeoutRtdProvider_spec.js +1 -201
@@ -1,488 +1,103 @@
1
1
  import { submodule } from '../src/hook.js';
2
- import { logError, logInfo, isPlainObject, isEmpty, isFn, mergeDeep } from '../src/utils.js';
3
- import { config as conf } from '../src/config.js';
4
- import { getDeviceType as fetchDeviceType, getOS } from '../libraries/userAgentUtils/index.js';
5
- import { getLowEntropySUA } from '../src/fpd/sua.js';
6
- import { getGlobal } from '../src/prebidGlobal.js';
7
- import { REJECTION_REASON } from '../src/constants.js';
2
+ import { logError, isStr, mergeDeep, isPlainObject, isEmpty } from '../src/utils.js';
3
+
4
+ import { PluginManager } from '../libraries/pubmaticUtils/plugins/pluginManager.js';
5
+ import { FloorProvider } from '../libraries/pubmaticUtils/plugins/floorProvider.js';
6
+ import { UnifiedPricingRule } from '../libraries/pubmaticUtils/plugins/unifiedPricingRule.js';
7
+ import { DynamicTimeout } from '../libraries/pubmaticUtils/plugins/dynamicTimeout.js';
8
8
 
9
9
  /**
10
- * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
10
+ * @typedef {import('./rtdModule/index.js').RtdSubmodule} RtdSubmodule
11
11
  */
12
-
13
12
  /**
14
13
  * This RTD module has a dependency on the priceFloors module.
15
14
  * We utilize the continueAuction function from the priceFloors module to incorporate price floors data into the current auction.
16
15
  */
17
- import { continueAuction } from './priceFloors.js'; // eslint-disable-line prebid/validate-imports
18
16
 
19
17
  export const CONSTANTS = Object.freeze({
20
18
  SUBMODULE_NAME: 'pubmatic',
21
19
  REAL_TIME_MODULE: 'realTimeData',
22
20
  LOG_PRE_FIX: 'PubMatic-Rtd-Provider: ',
23
- UTM: 'utm_',
24
- UTM_VALUES: {
25
- TRUE: '1',
26
- FALSE: '0'
27
- },
28
- TIME_OF_DAY_VALUES: {
29
- MORNING: 'morning',
30
- AFTERNOON: 'afternoon',
31
- EVENING: 'evening',
32
- NIGHT: 'night',
33
- },
34
21
  ENDPOINTS: {
35
22
  BASEURL: 'https://ads.pubmatic.com/AdServer/js/pwt',
36
- FLOORS: 'floors.json',
37
23
  CONFIGS: 'config.json'
38
- },
39
- BID_STATUS: {
40
- NOBID: 0,
41
- WON: 1,
42
- FLOORED: 2
43
- },
44
- MULTIPLIERS: {
45
- WIN: 1.0,
46
- FLOORED: 0.8,
47
- NOBID: 1.2
48
- },
49
- TARGETING_KEYS: {
50
- PM_YM_FLRS: 'pm_ym_flrs', // Whether RTD floor was applied
51
- PM_YM_FLRV: 'pm_ym_flrv', // Final floor value (after applying multiplier)
52
- PM_YM_BID_S: 'pm_ym_bid_s' // Bid status (0: No bid, 1: Won, 2: Floored)
53
24
  }
54
25
  });
55
26
 
56
- const BROWSER_REGEX_MAP = [
57
- { regex: /\b(?:crios)\/([\w\.]+)/i, id: 1 }, // Chrome for iOS
58
- { regex: /(edg|edge)(?:e|ios|a)?(?:\/([\w\.]+))?/i, id: 2 }, // Edge
59
- { regex: /(opera|opr)(?:.+version\/|[\/ ]+)([\w\.]+)/i, id: 3 }, // Opera
60
- { regex: /(?:ms|\()(ie) ([\w\.]+)|(?:trident\/[\w\.]+)/i, id: 4 }, // Internet Explorer
61
- { regex: /fxios\/([-\w\.]+)/i, id: 5 }, // Firefox for iOS
62
- { regex: /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i, id: 6 }, // Facebook In-App Browser
63
- { regex: / wv\).+(chrome)\/([\w\.]+)/i, id: 7 }, // Chrome WebView
64
- { regex: /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i, id: 8 }, // Android Browser
65
- { regex: /(chrome|crios)(?:\/v?([\w\.]+))?\b/i, id: 9 }, // Chrome
66
- { regex: /version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i, id: 10 }, // Safari Mobile
67
- { regex: /version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i, id: 11 }, // Safari
68
- { regex: /(firefox)\/([\w\.]+)/i, id: 12 } // Firefox
69
- ];
70
-
71
- export const defaultValueTemplate = {
72
- currency: 'USD',
73
- skipRate: 0,
74
- schema: {
75
- fields: ['mediaType', 'size']
76
- }
77
- };
78
-
79
- let initTime;
80
- let _fetchFloorRulesPromise = null; let _fetchConfigPromise = null;
81
- export let configMerged;
82
- // configMerged is a reference to the function that can resolve configMergedPromise whenever we want
83
- let configMergedPromise = new Promise((resolve) => { configMerged = resolve; });
84
- export let _country;
85
- // Store multipliers from floors.json, will use default values from CONSTANTS if not available
86
- export let _multipliers = null;
87
-
88
- // Use a private variable for profile configs
89
- let _profileConfigs;
90
- // Export getter and setter functions for _profileConfigs
91
- export const getProfileConfigs = () => _profileConfigs;
92
- export const setProfileConfigs = (configs) => { _profileConfigs = configs; };
93
-
94
- // Waits for a given promise to resolve within a timeout
95
- export function withTimeout(promise, ms) {
96
- let timeout;
97
- const timeoutPromise = new Promise((resolve) => {
98
- timeout = setTimeout(() => resolve(undefined), ms);
99
- });
100
-
101
- return Promise.race([promise.finally(() => clearTimeout(timeout)), timeoutPromise]);
102
- }
103
-
104
- // Utility Functions
105
- export const getCurrentTimeOfDay = () => {
106
- const currentHour = new Date().getHours();
107
-
108
- return currentHour < 5 ? CONSTANTS.TIME_OF_DAY_VALUES.NIGHT
109
- : currentHour < 12 ? CONSTANTS.TIME_OF_DAY_VALUES.MORNING
110
- : currentHour < 17 ? CONSTANTS.TIME_OF_DAY_VALUES.AFTERNOON
111
- : currentHour < 19 ? CONSTANTS.TIME_OF_DAY_VALUES.EVENING
112
- : CONSTANTS.TIME_OF_DAY_VALUES.NIGHT;
113
- }
114
-
115
- export const getBrowserType = () => {
116
- const brandName = getLowEntropySUA()?.browsers
117
- ?.map(b => b.brand.toLowerCase())
118
- .join(' ') || '';
119
- const browserMatch = brandName ? BROWSER_REGEX_MAP.find(({ regex }) => regex.test(brandName)) : -1;
120
-
121
- if (browserMatch?.id) return browserMatch.id.toString();
122
-
123
- const userAgent = navigator?.userAgent;
124
- let browserIndex = userAgent == null ? -1 : 0;
125
-
126
- if (userAgent) {
127
- browserIndex = BROWSER_REGEX_MAP.find(({ regex }) => regex.test(userAgent))?.id || 0;
128
- }
129
- return browserIndex.toString();
130
- }
27
+ let _ymConfigPromise;
28
+ export const getYmConfigPromise = () => _ymConfigPromise;
29
+ export const setYmConfigPromise = (promise) => { _ymConfigPromise = promise; };
131
30
 
132
- // Find all bids for a specific ad unit
133
- function findBidsForAdUnit(auction, code) {
134
- return auction?.bidsReceived?.filter(bid => bid.adUnitCode === code) || [];
135
- }
31
+ export function ConfigJsonManager() {
32
+ let _ymConfig = {};
33
+ const getYMConfig = () => _ymConfig;
34
+ const setYMConfig = (config) => { _ymConfig = config; }
35
+ let country;
136
36
 
137
- // Find rejected bids for a specific ad unit
138
- function findRejectedBidsForAdUnit(auction, code) {
139
- if (!auction?.bidsRejected) return [];
37
+ /**
38
+ * Fetch configuration from the server
39
+ * @param {string} publisherId - Publisher ID
40
+ * @param {string} profileId - Profile ID
41
+ * @returns {Promise<Object>} - Promise resolving to the config object
42
+ */
43
+ async function fetchConfig(publisherId, profileId) {
44
+ try {
45
+ const url = `${CONSTANTS.ENDPOINTS.BASEURL}/${publisherId}/${profileId}/${CONSTANTS.ENDPOINTS.CONFIGS}`;
46
+ const response = await fetch(url);
140
47
 
141
- // If bidsRejected is an array
142
- if (Array.isArray(auction.bidsRejected)) {
143
- return auction.bidsRejected.filter(bid => bid.adUnitCode === code);
144
- }
48
+ if (!response.ok) {
49
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error while fetching config: Not ok`);
50
+ return null;
51
+ }
145
52
 
146
- // If bidsRejected is an object mapping bidders to their rejected bids
147
- if (typeof auction.bidsRejected === 'object') {
148
- return Object.values(auction.bidsRejected)
149
- .filter(Array.isArray)
150
- .flatMap(bidderBids => bidderBids.filter(bid => bid.adUnitCode === code));
151
- }
53
+ // Extract country code if available
54
+ const cc = response.headers?.get('country_code');
55
+ country = cc ? cc.split(',')?.map(code => code.trim())[0] : "IN";
152
56
 
153
- return [];
154
- }
57
+ // Parse the JSON response
58
+ const ymConfigs = await response.json();
155
59
 
156
- // Find a rejected bid due to price floor
157
- function findRejectedFloorBid(rejectedBids) {
158
- return rejectedBids.find(bid => {
159
- return bid.rejectionReason === REJECTION_REASON.FLOOR_NOT_MET &&
160
- (bid.floorData?.floorValue && bid.cpm < bid.floorData.floorValue);
161
- });
162
- }
60
+ if (!isPlainObject(ymConfigs) || isEmpty(ymConfigs)) {
61
+ logError(`${CONSTANTS.LOG_PRE_FIX} profileConfigs is not an object or is empty`);
62
+ return null;
63
+ }
163
64
 
164
- // Find the winning or highest bid for an ad unit
165
- function findWinningBid(adUnitCode) {
166
- try {
167
- const pbjs = getGlobal();
168
- if (!pbjs?.getHighestCpmBids) return null;
65
+ // Store the configuration
66
+ setYMConfig(ymConfigs);
169
67
 
170
- const highestCpmBids = pbjs.getHighestCpmBids(adUnitCode);
171
- if (!highestCpmBids?.length) {
172
- logInfo(CONSTANTS.LOG_PRE_FIX, `No highest CPM bids found for ad unit: ${adUnitCode}`);
68
+ return true;
69
+ } catch (error) {
70
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error while fetching config: ${error}`);
173
71
  return null;
174
72
  }
175
-
176
- const highestCpmBid = highestCpmBids[0];
177
- logInfo(CONSTANTS.LOG_PRE_FIX, `Found highest CPM bid using pbjs.getHighestCpmBids() for ad unit: ${adUnitCode}, CPM: ${highestCpmBid.cpm}`);
178
- return highestCpmBid;
179
- } catch (error) {
180
- logError(CONSTANTS.LOG_PRE_FIX, `Error finding highest CPM bid: ${error}`);
181
- return null;
182
- }
183
- }
184
-
185
- // Find floor value from bidder requests
186
- function findFloorValueFromBidderRequests(auction, code) {
187
- if (!auction?.bidderRequests?.length) return 0;
188
-
189
- // Find all bids in bidder requests for this ad unit
190
- const bidsFromRequests = auction.bidderRequests
191
- .flatMap(request => request.bids || [])
192
- .filter(bid => bid.adUnitCode === code);
193
-
194
- if (!bidsFromRequests.length) {
195
- logInfo(CONSTANTS.LOG_PRE_FIX, `No bids found for ad unit: ${code}`);
196
- return 0;
197
- }
198
-
199
- const bidWithGetFloor = bidsFromRequests.find(bid => bid.getFloor);
200
- if (!bidWithGetFloor) {
201
- logInfo(CONSTANTS.LOG_PRE_FIX, `No bid with getFloor method found for ad unit: ${code}`);
202
- return 0;
203
- }
204
-
205
- // Helper function to extract sizes with their media types from a source object
206
- const extractSizes = (source) => {
207
- if (!source) return null;
208
-
209
- const result = [];
210
-
211
- // Extract banner sizes
212
- if (source.mediaTypes?.banner?.sizes) {
213
- source.mediaTypes.banner.sizes.forEach(size => {
214
- result.push({
215
- size,
216
- mediaType: 'banner'
217
- });
218
- });
219
- }
220
-
221
- // Extract video sizes
222
- if (source.mediaTypes?.video?.playerSize) {
223
- const playerSize = source.mediaTypes.video.playerSize;
224
- // Handle both formats: [[w, h]] and [w, h]
225
- const videoSizes = Array.isArray(playerSize[0]) ? playerSize : [playerSize];
226
-
227
- videoSizes.forEach(size => {
228
- result.push({
229
- size,
230
- mediaType: 'video'
231
- });
232
- });
233
- }
234
-
235
- // Use general sizes as fallback if no specific media types found
236
- if (result.length === 0 && source.sizes) {
237
- source.sizes.forEach(size => {
238
- result.push({
239
- size,
240
- mediaType: 'banner' // Default to banner for general sizes
241
- });
242
- });
243
- }
244
-
245
- return result.length > 0 ? result : null;
246
- };
247
-
248
- // Try to get sizes from different sources in order of preference
249
- const adUnit = auction.adUnits?.find(unit => unit.code === code);
250
- let sizes = extractSizes(adUnit) || extractSizes(bidWithGetFloor);
251
-
252
- // Handle fallback to wildcard size if no sizes found
253
- if (!sizes) {
254
- sizes = [{ size: ['*', '*'], mediaType: 'banner' }];
255
- logInfo(CONSTANTS.LOG_PRE_FIX, `No sizes found, using wildcard size for ad unit: ${code}`);
256
- }
257
-
258
- // Try to get floor values for each size
259
- let minFloor = -1;
260
-
261
- for (const sizeObj of sizes) {
262
- // Extract size and mediaType from the object
263
- const { size, mediaType } = sizeObj;
264
-
265
- // Call getFloor with the appropriate media type
266
- const floorInfo = bidWithGetFloor.getFloor({
267
- currency: 'USD', // Default currency
268
- mediaType: mediaType, // Use the media type we extracted
269
- size: size
270
- });
271
-
272
- if (floorInfo?.floor && !isNaN(parseFloat(floorInfo.floor))) {
273
- const floorValue = parseFloat(floorInfo.floor);
274
- logInfo(CONSTANTS.LOG_PRE_FIX, `Floor value for ${mediaType} size ${size}: ${floorValue}`);
275
-
276
- // Update minimum floor value
277
- minFloor = minFloor === -1 ? floorValue : Math.min(minFloor, floorValue);
278
- }
279
73
  }
280
74
 
281
- if (minFloor !== -1) {
282
- logInfo(CONSTANTS.LOG_PRE_FIX, `Calculated minimum floor value ${minFloor} for ad unit: ${code}`);
283
- return minFloor;
284
- }
285
-
286
- logInfo(CONSTANTS.LOG_PRE_FIX, `No floor data found for ad unit: ${code}`);
287
- return 0;
288
- }
289
-
290
- // Select multiplier based on priority order: floors.json → config.json → default
291
- function selectMultiplier(multiplierKey, profileConfigs) {
292
- // Define sources in priority order
293
- const multiplierSources = [
294
- {
295
- name: 'config.json',
296
- getValue: () => {
297
- const configPath = profileConfigs?.plugins?.dynamicFloors?.pmTargetingKeys?.multiplier;
298
- const lowerKey = multiplierKey.toLowerCase();
299
- return configPath && lowerKey in configPath ? configPath[lowerKey] : null;
300
- }
301
- },
302
- {
303
- name: 'floor.json',
304
- getValue: () => _multipliers && multiplierKey in _multipliers ? _multipliers[multiplierKey] : null
305
- },
306
- {
307
- name: 'default',
308
- getValue: () => CONSTANTS.MULTIPLIERS[multiplierKey]
309
- }
310
- ];
311
-
312
- // Find the first source with a non-null value
313
- for (const source of multiplierSources) {
314
- const value = source.getValue();
315
- if (value != null) {
316
- return { value, source: source.name };
317
- }
75
+ /**
76
+ * Get configuration by name
77
+ * @param {string} name - Plugin name
78
+ * @returns {Object} - Plugin configuration
79
+ */
80
+ const getConfigByName = (name) => {
81
+ return getYMConfig()?.plugins?.[name];
318
82
  }
319
83
 
320
- // Fallback (shouldn't happen due to default source)
321
- return { value: CONSTANTS.MULTIPLIERS[multiplierKey], source: 'default' };
322
- }
323
-
324
- // Identify winning bid scenario and return scenario data
325
- function handleWinningBidScenario(winningBid, code) {
326
- return {
327
- scenario: 'winning',
328
- bidStatus: CONSTANTS.BID_STATUS.WON,
329
- baseValue: winningBid.cpm,
330
- multiplierKey: 'WIN',
331
- logMessage: `Bid won for ad unit: ${code}, CPM: ${winningBid.cpm}`
332
- };
333
- }
334
-
335
- // Identify rejected floor bid scenario and return scenario data
336
- function handleRejectedFloorBidScenario(rejectedFloorBid, code) {
337
- const baseValue = rejectedFloorBid.floorData?.floorValue || 0;
338
- return {
339
- scenario: 'rejected',
340
- bidStatus: CONSTANTS.BID_STATUS.FLOORED,
341
- baseValue,
342
- multiplierKey: 'FLOORED',
343
- logMessage: `Bid rejected due to price floor for ad unit: ${code}, Floor value: ${baseValue}, Bid CPM: ${rejectedFloorBid.cpm}`
344
- };
345
- }
346
-
347
- // Identify no bid scenario and return scenario data
348
- function handleNoBidScenario(auction, code) {
349
- const baseValue = findFloorValueFromBidderRequests(auction, code);
350
84
  return {
351
- scenario: 'nobid',
352
- bidStatus: CONSTANTS.BID_STATUS.NOBID,
353
- baseValue,
354
- multiplierKey: 'NOBID',
355
- logMessage: `No bids for ad unit: ${code}, Floor value: ${baseValue}`
85
+ fetchConfig,
86
+ getYMConfig,
87
+ setYMConfig,
88
+ getConfigByName,
89
+ get country() { return country; }
356
90
  };
357
91
  }
358
92
 
359
- // Determine which scenario applies based on bid conditions
360
- function determineScenario(winningBid, rejectedFloorBid, bidsForAdUnit, auction, code) {
361
- return winningBid ? handleWinningBidScenario(winningBid, code)
362
- : rejectedFloorBid ? handleRejectedFloorBidScenario(rejectedFloorBid, code)
363
- : handleNoBidScenario(auction, code);
364
- }
365
-
366
- // Main function that determines bid status and calculates values
367
- function determineBidStatusAndValues(winningBid, rejectedFloorBid, bidsForAdUnit, auction, code) {
368
- const profileConfigs = getProfileConfigs();
369
-
370
- // Determine the scenario based on bid conditions
371
- const { bidStatus, baseValue, multiplierKey, logMessage } =
372
- determineScenario(winningBid, rejectedFloorBid, bidsForAdUnit, auction, code);
373
-
374
- // Select the appropriate multiplier
375
- const { value: multiplier, source } = selectMultiplier(multiplierKey, profileConfigs);
376
- logInfo(CONSTANTS.LOG_PRE_FIX, logMessage + ` (Using ${source} multiplier: ${multiplier})`);
377
-
378
- return { bidStatus, baseValue, multiplier };
379
- }
380
-
381
- // Getter Functions
382
- export const getOs = () => getOS().toString();
383
- export const getDeviceType = () => fetchDeviceType().toString();
384
- export const getCountry = () => _country;
385
- export const getBidder = (request) => request?.bidder;
386
- export const getUtm = () => {
387
- const url = new URL(window.location?.href);
388
- const urlParams = new URLSearchParams(url?.search);
389
- return urlParams && urlParams.toString().includes(CONSTANTS.UTM) ? CONSTANTS.UTM_VALUES.TRUE : CONSTANTS.UTM_VALUES.FALSE;
390
- }
391
-
392
- export const getFloorsConfig = (floorsData, profileConfigs) => {
393
- if (!isPlainObject(profileConfigs) || isEmpty(profileConfigs)) {
394
- logError(`${CONSTANTS.LOG_PRE_FIX} profileConfigs is not an object or is empty`);
395
- return undefined;
396
- }
397
-
398
- // Floor configs from adunit / setconfig
399
- const defaultFloorConfig = conf.getConfig('floors') ?? {};
400
- if (defaultFloorConfig?.endpoint) {
401
- delete defaultFloorConfig.endpoint;
402
- }
403
- // Plugin data from profile
404
- const dynamicFloors = profileConfigs?.plugins?.dynamicFloors;
405
-
406
- // If plugin disabled or config not present, return undefined
407
- if (!dynamicFloors?.enabled || !dynamicFloors?.config) {
408
- return undefined;
409
- }
410
-
411
- let config = { ...dynamicFloors.config };
412
-
413
- // default values provided by publisher on profile
414
- const defaultValues = config.defaultValues ?? {};
415
- // If floorsData is not present, use default values
416
- const finalFloorsData = floorsData ?? { ...defaultValueTemplate, values: { ...defaultValues } };
417
-
418
- delete config.defaultValues;
419
- // If skiprate is provided in configs, overwrite the value in finalFloorsData
420
- (config.skipRate !== undefined) && (finalFloorsData.skipRate = config.skipRate);
421
-
422
- // merge default configs from page, configs
423
- return {
424
- floors: {
425
- ...defaultFloorConfig,
426
- ...config,
427
- data: finalFloorsData,
428
- additionalSchemaFields: {
429
- deviceType: getDeviceType,
430
- timeOfDay: getCurrentTimeOfDay,
431
- browser: getBrowserType,
432
- os: getOs,
433
- utm: getUtm,
434
- country: getCountry,
435
- bidder: getBidder,
436
- },
437
- },
438
- };
439
- };
440
-
441
- export const fetchData = async (publisherId, profileId, type) => {
442
- try {
443
- const endpoint = CONSTANTS.ENDPOINTS[type];
444
- const baseURL = (type == 'FLOORS') ? `${CONSTANTS.ENDPOINTS.BASEURL}/floors` : CONSTANTS.ENDPOINTS.BASEURL;
445
- const url = `${baseURL}/${publisherId}/${profileId}/${endpoint}`;
446
- const response = await fetch(url);
447
-
448
- if (!response.ok) {
449
- logError(`${CONSTANTS.LOG_PRE_FIX} Error while fetching ${type}: Not ok`);
450
- return;
451
- }
452
-
453
- if (type === "FLOORS") {
454
- const cc = response.headers?.get('country_code');
455
- _country = cc ? cc.split(',')?.map(code => code.trim())[0] : undefined;
456
- }
457
-
458
- const data = await response.json();
93
+ // Create core components
94
+ export const pluginManager = PluginManager();
95
+ export const configJsonManager = ConfigJsonManager();
459
96
 
460
- // Extract multipliers from floors.json if available
461
- if (type === "FLOORS" && data && data.multiplier) {
462
- // Map of source keys to destination keys
463
- const multiplierKeys = {
464
- 'win': 'WIN',
465
- 'floored': 'FLOORED',
466
- 'nobid': 'NOBID'
467
- };
468
-
469
- // Initialize _multipliers and only add keys that exist in data.multiplier
470
- _multipliers = Object.entries(multiplierKeys)
471
- .reduce((acc, [srcKey, destKey]) => {
472
- if (srcKey in data.multiplier) {
473
- acc[destKey] = data.multiplier[srcKey];
474
- }
475
- return acc;
476
- }, {});
477
-
478
- logInfo(CONSTANTS.LOG_PRE_FIX, `Using multipliers from floors.json: ${JSON.stringify(_multipliers)}`);
479
- }
480
-
481
- return data;
482
- } catch (error) {
483
- logError(`${CONSTANTS.LOG_PRE_FIX} Error while fetching ${type}: ${error}`);
484
- }
485
- };
97
+ // Register plugins
98
+ pluginManager.register('dynamicFloors', FloorProvider);
99
+ pluginManager.register('unifiedPricingRule', UnifiedPricingRule);
100
+ pluginManager.register('dynamicTimeout', DynamicTimeout);
486
101
 
487
102
  /**
488
103
  * Initialize the Pubmatic RTD Module.
@@ -491,44 +106,28 @@ export const fetchData = async (publisherId, profileId, type) => {
491
106
  * @returns {boolean}
492
107
  */
493
108
  const init = (config, _userConsent) => {
494
- initTime = Date.now(); // Capture the initialization time
495
- let { publisherId, profileId } = config?.params || {};
496
-
497
- if (!publisherId || !profileId) {
498
- logError(
499
- `${CONSTANTS.LOG_PRE_FIX} ${!publisherId ? 'Missing publisher Id.' : 'Missing profile Id.'}`
500
- );
501
- return false;
502
- }
503
-
504
- publisherId = String(publisherId).trim();
505
- profileId = String(profileId).trim();
506
-
507
- if (!isFn(continueAuction)) {
508
- logError(`${CONSTANTS.LOG_PRE_FIX} continueAuction is not a function. Please ensure to add priceFloors module.`);
509
- return false;
510
- }
511
-
512
- _fetchFloorRulesPromise = fetchData(publisherId, profileId, "FLOORS");
513
- _fetchConfigPromise = fetchData(publisherId, profileId, "CONFIGS");
514
-
515
- _fetchConfigPromise.then(async (profileConfigs) => {
516
- const auctionDelay = conf?.getConfig('realTimeData')?.auctionDelay || 300;
517
- const maxWaitTime = 0.8 * auctionDelay;
518
-
519
- const elapsedTime = Date.now() - initTime;
520
- const remainingTime = Math.max(maxWaitTime - elapsedTime, 0);
521
- const floorsData = await withTimeout(_fetchFloorRulesPromise, remainingTime);
522
-
523
- // Store the profile configs globally
524
- setProfileConfigs(profileConfigs);
109
+ const { publisherId, profileId } = config?.params || {};
110
+
111
+ if (!publisherId || !isStr(publisherId) || !profileId || !isStr(profileId)) {
112
+ logError(
113
+ `${CONSTANTS.LOG_PRE_FIX} ${!publisherId ? 'Missing publisher Id.'
114
+ : !isStr(publisherId) ? 'Publisher Id should be a string.'
115
+ : !profileId ? 'Missing profile Id.'
116
+ : 'Profile Id should be a string.'
117
+ }`
118
+ );
119
+ return false;
120
+ }
525
121
 
526
- const floorsConfig = getFloorsConfig(floorsData, profileConfigs);
527
- floorsConfig && conf?.setConfig(floorsConfig);
528
- configMerged();
122
+ // Fetch configuration and initialize plugins
123
+ _ymConfigPromise = configJsonManager.fetchConfig(publisherId, profileId)
124
+ .then(success => {
125
+ if (!success) {
126
+ return Promise.reject(new Error('Failed to fetch configuration'));
127
+ }
128
+ return pluginManager.initialize(configJsonManager);
529
129
  });
530
-
531
- return true;
130
+ return true;
532
131
  };
533
132
 
534
133
  /**
@@ -536,34 +135,30 @@ const init = (config, _userConsent) => {
536
135
  * @param {function} callback
537
136
  */
538
137
  const getBidRequestData = (reqBidsConfigObj, callback) => {
539
- configMergedPromise.then(() => {
540
- const hookConfig = {
541
- reqBidsConfigObj,
542
- context: this,
543
- nextFn: () => true,
544
- haveExited: false,
545
- timer: null
546
- };
547
- continueAuction(hookConfig);
548
- if (_country) {
549
- const ortb2 = {
550
- user: {
551
- ext: {
552
- ctr: _country,
553
- }
554
- }
138
+ _ymConfigPromise.then(() => {
139
+ pluginManager.executeHook('processBidRequest', reqBidsConfigObj);
140
+ // Apply country information if available
141
+ const country = configJsonManager.country;
142
+ if (country) {
143
+ const ortb2 = {
144
+ user: {
145
+ ext: {
146
+ ctr: country,
555
147
  }
556
-
557
- mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, {
558
- [CONSTANTS.SUBMODULE_NAME]: ortb2
559
- });
560
148
  }
561
- callback();
562
- }).catch((error) => {
563
- logError(CONSTANTS.LOG_PRE_FIX, 'Error in updating floors :', error);
564
- callback();
565
- });
566
- }
149
+ };
150
+
151
+ mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, {
152
+ [CONSTANTS.SUBMODULE_NAME]: ortb2
153
+ });
154
+ }
155
+
156
+ callback();
157
+ }).catch(error => {
158
+ logError(CONSTANTS.LOG_PRE_FIX, error);
159
+ callback();
160
+ });
161
+ };
567
162
 
568
163
  /**
569
164
  * Returns targeting data for ad units
@@ -574,62 +169,7 @@ const getBidRequestData = (reqBidsConfigObj, callback) => {
574
169
  * @return {Object} - Targeting data for ad units
575
170
  */
576
171
  export const getTargetingData = (adUnitCodes, config, userConsent, auction) => {
577
- // Access the profile configs stored globally
578
- const profileConfigs = getProfileConfigs();
579
-
580
- // Return empty object if profileConfigs is undefined or pmTargetingKeys.enabled is explicitly set to false
581
- if (!profileConfigs || profileConfigs?.plugins?.dynamicFloors?.pmTargetingKeys?.enabled === false) {
582
- logInfo(`${CONSTANTS.LOG_PRE_FIX} pmTargetingKeys is disabled or profileConfigs is undefined`);
583
- return {};
584
- }
585
-
586
- // Helper to check if RTD floor is applied to a bid
587
- const isRtdFloorApplied = bid => bid.floorData?.floorProvider === "PM" && !bid.floorData.skipped;
588
-
589
- // Check if any bid has RTD floor applied
590
- const hasRtdFloorAppliedBid =
591
- auction?.adUnits?.some(adUnit => adUnit.bids?.some(isRtdFloorApplied)) ||
592
- auction?.bidsReceived?.some(isRtdFloorApplied);
593
-
594
- // Only log when RTD floor is applied
595
- if (hasRtdFloorAppliedBid) {
596
- logInfo(CONSTANTS.LOG_PRE_FIX, 'Setting targeting via getTargetingData:');
597
- }
598
-
599
- // Process each ad unit code
600
- const targeting = {};
601
-
602
- adUnitCodes.forEach(code => {
603
- targeting[code] = {};
604
-
605
- // For non-RTD floor applied cases, only set pm_ym_flrs to 0
606
- if (!hasRtdFloorAppliedBid) {
607
- targeting[code][CONSTANTS.TARGETING_KEYS.PM_YM_FLRS] = 0;
608
- return;
609
- }
610
-
611
- // Find bids and determine status for RTD floor applied cases
612
- const bidsForAdUnit = findBidsForAdUnit(auction, code);
613
- const rejectedBidsForAdUnit = findRejectedBidsForAdUnit(auction, code);
614
- const rejectedFloorBid = findRejectedFloorBid(rejectedBidsForAdUnit);
615
- const winningBid = findWinningBid(code);
616
-
617
- // Determine bid status and values
618
- const { bidStatus, baseValue, multiplier } = determineBidStatusAndValues(
619
- winningBid,
620
- rejectedFloorBid,
621
- bidsForAdUnit,
622
- auction,
623
- code
624
- );
625
-
626
- // Set all targeting keys
627
- targeting[code][CONSTANTS.TARGETING_KEYS.PM_YM_FLRS] = 1;
628
- targeting[code][CONSTANTS.TARGETING_KEYS.PM_YM_FLRV] = (baseValue * multiplier).toFixed(2);
629
- targeting[code][CONSTANTS.TARGETING_KEYS.PM_YM_BID_S] = bidStatus;
630
- });
631
-
632
- return targeting;
172
+ return pluginManager.executeHook('getTargeting', adUnitCodes, config, userConsent, auction);
633
173
  };
634
174
 
635
175
  export const pubmaticSubmodule = {