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
@@ -0,0 +1,168 @@
1
+ // plugins/floorProvider.js
2
+ import { logInfo, logError, isFn, logMessage, isEmpty } from '../../../src/utils.js';
3
+ import { getDeviceType as fetchDeviceType, getOS } from '../../userAgentUtils/index.js';
4
+ import { getBrowserType, getCurrentTimeOfDay, getUtmValue } from '../pubmaticUtils.js';
5
+ import { config as conf } from '../../../src/config.js';
6
+
7
+ /**
8
+ * This RTD module has a dependency on the priceFloors module.
9
+ * We utilize the continueAuction function from the priceFloors module to incorporate price floors data into the current auction.
10
+ */
11
+ import { continueAuction } from '../../../modules/priceFloors.js'; // eslint-disable-line prebid/validate-imports
12
+
13
+ let _floorConfig = null;
14
+ export const getFloorConfig = () => _floorConfig;
15
+ export const setFloorsConfig = (config) => { _floorConfig = config; }
16
+
17
+ let _configJsonManager = null;
18
+ export const getConfigJsonManager = () => _configJsonManager;
19
+ export const setConfigJsonManager = (configJsonManager) => { _configJsonManager = configJsonManager; }
20
+
21
+ export const CONSTANTS = Object.freeze({
22
+ LOG_PRE_FIX: 'PubMatic-Floor-Provider: '
23
+ });
24
+
25
+ /**
26
+ * Initialize the floor provider
27
+ * @param {Object} pluginName - Plugin name
28
+ * @param {Object} configJsonManager - Configuration JSON manager object
29
+ * @returns {Promise<boolean>} - Promise resolving to initialization status
30
+ */
31
+ export async function init(pluginName, configJsonManager) {
32
+ // Process floor-specific configuration
33
+ const config = configJsonManager.getConfigByName(pluginName);
34
+ if (!config) {
35
+ logInfo(`${CONSTANTS.LOG_PRE_FIX} Floor configuration not found`);
36
+ return false;
37
+ }
38
+ setFloorsConfig(config);
39
+
40
+ if (!getFloorConfig()?.enabled) {
41
+ logInfo(`${CONSTANTS.LOG_PRE_FIX} Floor configuration is disabled`);
42
+ return false;
43
+ }
44
+
45
+ if (!isFn(continueAuction)) {
46
+ logError(`${CONSTANTS.LOG_PRE_FIX} continueAuction is not a function. Please ensure to add priceFloors module.`);
47
+ return false;
48
+ }
49
+
50
+ setConfigJsonManager(configJsonManager);
51
+ try {
52
+ conf.setConfig(prepareFloorsConfig());
53
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} dynamicFloors config set successfully`);
54
+ } catch (error) {
55
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error setting dynamicFloors config: ${error}`);
56
+ }
57
+
58
+ logInfo(`${CONSTANTS.LOG_PRE_FIX} Floor configuration loaded`);
59
+
60
+ return true;
61
+ }
62
+
63
+ /**
64
+ * Process bid request
65
+ * @param {Object} reqBidsConfigObj - Bid request config object
66
+ * @returns {Object} - Updated bid request config object
67
+ */
68
+ export function processBidRequest(reqBidsConfigObj) {
69
+ try {
70
+ const hookConfig = {
71
+ reqBidsConfigObj,
72
+ context: null, // Removed 'this' as it's not applicable in function-based implementation
73
+ nextFn: () => true,
74
+ haveExited: false,
75
+ timer: null
76
+ };
77
+
78
+ // Apply floor configuration
79
+ continueAuction(hookConfig);
80
+ logInfo(`${CONSTANTS.LOG_PRE_FIX} Applied floor configuration to auction`);
81
+
82
+ return reqBidsConfigObj;
83
+ } catch (error) {
84
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error applying floor configuration: ${error}`);
85
+ return reqBidsConfigObj;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Get targeting data
91
+ * @param {Array} adUnitCodes - Ad unit codes
92
+ * @param {Object} config - Module configuration
93
+ * @param {Object} userConsent - User consent data
94
+ * @param {Object} auction - Auction object
95
+ * @returns {Object} - Targeting data
96
+ */
97
+ export function getTargeting(adUnitCodes, config, userConsent, auction) {
98
+ // Implementation for targeting data, if not applied then do nothing
99
+ }
100
+
101
+ // Export the floor provider functions
102
+ export const FloorProvider = {
103
+ init,
104
+ processBidRequest,
105
+ getTargeting
106
+ };
107
+
108
+ // Helper Functions
109
+
110
+ export const defaultValueTemplate = {
111
+ currency: 'USD',
112
+ skipRate: 0,
113
+ schema: {
114
+ fields: ['mediaType', 'size']
115
+ }
116
+ };
117
+
118
+ // Getter Functions
119
+ export const getTimeOfDay = () => getCurrentTimeOfDay();
120
+ export const getBrowser = () => getBrowserType();
121
+ export const getOs = () => getOS().toString();
122
+ export const getDeviceType = () => fetchDeviceType().toString();
123
+ export const getCountry = () => getConfigJsonManager().country;
124
+ export const getBidder = (request) => request?.bidder;
125
+ export const getUtm = () => getUtmValue();
126
+
127
+ export const prepareFloorsConfig = () => {
128
+ if (!getFloorConfig()?.enabled || !getFloorConfig()?.config) {
129
+ return undefined;
130
+ }
131
+
132
+ // Floor configs from adunit / setconfig
133
+ const defaultFloorConfig = conf.getConfig('floors') ?? {};
134
+ if (defaultFloorConfig?.endpoint) {
135
+ delete defaultFloorConfig.endpoint;
136
+ }
137
+
138
+ let ymUiConfig = { ...getFloorConfig().config };
139
+
140
+ // default values provided by publisher on YM UI
141
+ const defaultValues = ymUiConfig.defaultValues ?? {};
142
+ // If floorsData is not present or is an empty object, use default values
143
+ const ymFloorsData = isEmpty(getFloorConfig().data)
144
+ ? { ...defaultValueTemplate, values: { ...defaultValues } }
145
+ : getFloorConfig().data;
146
+
147
+ delete ymUiConfig.defaultValues;
148
+ // If skiprate is provided in configs, overwrite the value in ymFloorsData
149
+ (ymUiConfig.skipRate !== undefined) && (ymFloorsData.skipRate = ymUiConfig.skipRate);
150
+
151
+ // merge default configs from page, configs
152
+ return {
153
+ floors: {
154
+ ...defaultFloorConfig,
155
+ ...ymUiConfig,
156
+ data: ymFloorsData,
157
+ additionalSchemaFields: {
158
+ deviceType: getDeviceType,
159
+ timeOfDay: getTimeOfDay,
160
+ browser: getBrowser,
161
+ os: getOs,
162
+ utm: getUtm,
163
+ country: getCountry,
164
+ bidder: getBidder,
165
+ },
166
+ },
167
+ };
168
+ };
@@ -0,0 +1,106 @@
1
+ import { logInfo, logWarn, logError } from "../../../src/utils.js";
2
+
3
+ // pluginManager.js
4
+ export const plugins = new Map();
5
+ export const CONSTANTS = Object.freeze({
6
+ LOG_PRE_FIX: 'PubMatic-Plugin-Manager: '
7
+ });
8
+
9
+ /**
10
+ * Initialize the plugin manager with constants
11
+ * @returns {Object} - Plugin manager functions
12
+ */
13
+ export const PluginManager = () => ({
14
+ register,
15
+ initialize,
16
+ executeHook
17
+ });
18
+
19
+ /**
20
+ * Register a plugin with the plugin manager
21
+ * @param {string} name - Plugin name
22
+ * @param {Object} plugin - Plugin object
23
+ * @returns {Object} - Plugin manager functions
24
+ */
25
+ const register = (name, plugin) => {
26
+ if (plugins.has(name)) {
27
+ logWarn(`${CONSTANTS.LOG_PRE_FIX} Plugin ${name} already registered`);
28
+ return;
29
+ }
30
+ plugins.set(name, plugin);
31
+ };
32
+
33
+ /**
34
+ * Unregister a plugin from the plugin manager
35
+ * @param {string} name - Plugin name
36
+ * @returns {Object} - Plugin manager functions
37
+ */
38
+ const unregister = (name) => {
39
+ if (plugins.has(name)) {
40
+ logInfo(`${CONSTANTS.LOG_PRE_FIX} Unregistering plugin ${name}`);
41
+ plugins.delete(name);
42
+ }
43
+ };
44
+
45
+ /**
46
+ * Initialize all registered plugins with their specific config
47
+ * @param {Object} configJsonManager - Configuration JSON manager object
48
+ * @returns {Promise} - Promise resolving when all plugins are initialized
49
+ */
50
+ const initialize = async (configJsonManager) => {
51
+ const initPromises = [];
52
+
53
+ // Initialize each plugin with its specific config
54
+ for (const [name, plugin] of plugins.entries()) {
55
+ if (plugin.init) {
56
+ const initialized = await plugin.init(name, configJsonManager);
57
+ if (!initialized) {
58
+ unregister(name);
59
+ }
60
+ initPromises.push(initialized);
61
+ }
62
+ }
63
+
64
+ return Promise.all(initPromises);
65
+ };
66
+
67
+ /**
68
+ * Execute a hook on all registered plugins synchronously
69
+ * @param {string} hookName - Name of the hook to execute
70
+ * @param {...any} args - Arguments to pass to the hook
71
+ * @returns {Object} - Object containing merged results from all plugins
72
+ */
73
+ const executeHook = (hookName, ...args) => {
74
+ // Cache results to avoid repeated processing
75
+ const results = {};
76
+
77
+ try {
78
+ // Get all plugins that have the specified hook method
79
+ const pluginsWithHook = Array.from(plugins.entries())
80
+ .filter(([_, plugin]) => typeof plugin[hookName] === 'function');
81
+
82
+ // Process each plugin synchronously
83
+ for (const [name, plugin] of pluginsWithHook) {
84
+ try {
85
+ // Call the plugin's hook method synchronously
86
+ const result = plugin[hookName](...args);
87
+
88
+ // Skip null/undefined results
89
+ if (result === null || result === undefined) {
90
+ continue;
91
+ }
92
+
93
+ // If result is an object, merge it
94
+ if (typeof result === 'object') {
95
+ Object.assign(results, result);
96
+ }
97
+ } catch (error) {
98
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error executing hook ${hookName} in plugin ${name}: ${error.message}`);
99
+ }
100
+ }
101
+ } catch (error) {
102
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error in executeHookSync: ${error.message}`);
103
+ }
104
+
105
+ return results;
106
+ };
@@ -0,0 +1,375 @@
1
+ // plugins/unifiedPricingRule.js
2
+ import { logError, logInfo } from '../../../src/utils.js';
3
+ import { getGlobal } from '../../../src/prebidGlobal.js';
4
+ import { REJECTION_REASON } from '../../../src/constants.js';
5
+
6
+ const CONSTANTS = Object.freeze({
7
+ LOG_PRE_FIX: 'PubMatic-Unified-Pricing-Rule: ',
8
+ BID_STATUS: {
9
+ NOBID: 0,
10
+ WON: 1,
11
+ FLOORED: 2
12
+ },
13
+ MULTIPLIERS: {
14
+ WIN: 1.0,
15
+ FLOORED: 1.0,
16
+ NOBID: 1.0
17
+ },
18
+ TARGETING_KEYS: {
19
+ PM_YM_FLRS: 'pm_ym_flrs', // Whether RTD floor was applied
20
+ PM_YM_FLRV: 'pm_ym_flrv', // Final floor value (after applying multiplier)
21
+ PM_YM_BID_S: 'pm_ym_bid_s' // Bid status (0: No bid, 1: Won, 2: Floored)
22
+ }
23
+ });
24
+ export const getProfileConfigs = () => getConfigJsonManager()?.getYMConfig();
25
+
26
+ let _configJsonManager = null;
27
+ export const getConfigJsonManager = () => _configJsonManager;
28
+ export const setConfigJsonManager = (configJsonManager) => { _configJsonManager = configJsonManager; }
29
+
30
+ /**
31
+ * Initialize the floor provider
32
+ * @param {Object} pluginName - Plugin name
33
+ * @param {Object} configJsonManager - Configuration JSON manager object
34
+ * @returns {Promise<boolean>} - Promise resolving to initialization status
35
+ */
36
+ export async function init(pluginName, configJsonManager) {
37
+ setConfigJsonManager(configJsonManager);
38
+ return true;
39
+ }
40
+
41
+ /**
42
+ * Process bid request
43
+ * @param {Object} reqBidsConfigObj - Bid request config object
44
+ * @returns {Object} - Updated bid request config object
45
+ */
46
+ export function processBidRequest(reqBidsConfigObj) {
47
+ return reqBidsConfigObj;
48
+ }
49
+
50
+ /**
51
+ * Get targeting data
52
+ * @param {Array} adUnitCodes - Ad unit codes
53
+ * @param {Object} config - Module configuration
54
+ * @param {Object} userConsent - User consent data
55
+ * @param {Object} auction - Auction object
56
+ * @returns {Object} - Targeting data
57
+ */
58
+ export function getTargeting(adUnitCodes, config, userConsent, auction) {
59
+ // Access the profile configs stored globally
60
+ const profileConfigs = getProfileConfigs();
61
+
62
+ // Return empty object if profileConfigs is undefined or pmTargetingKeys.enabled is explicitly set to false
63
+ if (!profileConfigs || profileConfigs?.plugins?.dynamicFloors?.pmTargetingKeys?.enabled === false) {
64
+ logInfo(`${CONSTANTS.LOG_PRE_FIX} pmTargetingKeys is disabled or profileConfigs is undefined`);
65
+ return {};
66
+ }
67
+
68
+ // Helper to check if RTD floor is applied to a bid
69
+ const isRtdFloorApplied = bid => bid.floorData?.floorProvider === "PM" && !bid.floorData.skipped;
70
+
71
+ // Check if any bid has RTD floor applied
72
+ const hasRtdFloorAppliedBid =
73
+ auction?.adUnits?.some(adUnit => adUnit.bids?.some(isRtdFloorApplied)) ||
74
+ auction?.bidsReceived?.some(isRtdFloorApplied);
75
+
76
+ // Only log when RTD floor is applied
77
+ if (hasRtdFloorAppliedBid) {
78
+ logInfo(CONSTANTS.LOG_PRE_FIX, 'Setting targeting via getTargetingData:');
79
+ }
80
+
81
+ // Process each ad unit code
82
+ const targeting = {};
83
+
84
+ adUnitCodes.forEach(code => {
85
+ targeting[code] = {};
86
+
87
+ // For non-RTD floor applied cases, only set pm_ym_flrs to 0
88
+ if (!hasRtdFloorAppliedBid) {
89
+ targeting[code][CONSTANTS.TARGETING_KEYS.PM_YM_FLRS] = 0;
90
+ return;
91
+ }
92
+
93
+ // Find bids and determine status for RTD floor applied cases
94
+ const bidsForAdUnit = findBidsForAdUnit(auction, code);
95
+ const rejectedBidsForAdUnit = findRejectedBidsForAdUnit(auction, code);
96
+ const rejectedFloorBid = findRejectedFloorBid(rejectedBidsForAdUnit);
97
+ const winningBid = findWinningBid(code);
98
+
99
+ // Determine bid status and values
100
+ const { bidStatus, baseValue, multiplier } = determineBidStatusAndValues(
101
+ winningBid,
102
+ rejectedFloorBid,
103
+ bidsForAdUnit,
104
+ auction,
105
+ code
106
+ );
107
+
108
+ // Set all targeting keys
109
+ targeting[code][CONSTANTS.TARGETING_KEYS.PM_YM_FLRS] = 1;
110
+ targeting[code][CONSTANTS.TARGETING_KEYS.PM_YM_FLRV] = (baseValue * multiplier).toFixed(2);
111
+ targeting[code][CONSTANTS.TARGETING_KEYS.PM_YM_BID_S] = bidStatus;
112
+ });
113
+
114
+ return targeting;
115
+ }
116
+
117
+ // Export the floor provider functions
118
+ export const UnifiedPricingRule = {
119
+ init,
120
+ processBidRequest,
121
+ getTargeting
122
+ };
123
+
124
+ // Find all bids for a specific ad unit
125
+ function findBidsForAdUnit(auction, code) {
126
+ return auction?.bidsReceived?.filter(bid => bid.adUnitCode === code) || [];
127
+ }
128
+
129
+ // Find rejected bids for a specific ad unit
130
+ function findRejectedBidsForAdUnit(auction, code) {
131
+ if (!auction?.bidsRejected) return [];
132
+
133
+ // If bidsRejected is an array
134
+ if (Array.isArray(auction.bidsRejected)) {
135
+ return auction.bidsRejected.filter(bid => bid.adUnitCode === code);
136
+ }
137
+
138
+ // If bidsRejected is an object mapping bidders to their rejected bids
139
+ if (typeof auction.bidsRejected === 'object') {
140
+ return Object.values(auction.bidsRejected)
141
+ .filter(Array.isArray)
142
+ .flatMap(bidderBids => bidderBids.filter(bid => bid.adUnitCode === code));
143
+ }
144
+
145
+ return [];
146
+ }
147
+
148
+ // Find a rejected bid due to price floor
149
+ function findRejectedFloorBid(rejectedBids) {
150
+ return rejectedBids.find(bid => {
151
+ return bid.rejectionReason === REJECTION_REASON.FLOOR_NOT_MET &&
152
+ (bid.floorData?.floorValue && bid.cpm < bid.floorData.floorValue);
153
+ });
154
+ }
155
+
156
+ // Find the winning or highest bid for an ad unit
157
+ function findWinningBid(adUnitCode) {
158
+ try {
159
+ const pbjs = getGlobal();
160
+ if (!pbjs?.getHighestCpmBids) return null;
161
+
162
+ const highestCpmBids = pbjs.getHighestCpmBids(adUnitCode);
163
+ if (!highestCpmBids?.length) {
164
+ logInfo(CONSTANTS.LOG_PRE_FIX, `No highest CPM bids found for ad unit: ${adUnitCode}`);
165
+ return null;
166
+ }
167
+
168
+ const highestCpmBid = highestCpmBids[0];
169
+ logInfo(CONSTANTS.LOG_PRE_FIX, `Found highest CPM bid using pbjs.getHighestCpmBids() for ad unit: ${adUnitCode}, CPM: ${highestCpmBid.cpm}`);
170
+ return highestCpmBid;
171
+ } catch (error) {
172
+ logError(CONSTANTS.LOG_PRE_FIX, `Error finding highest CPM bid: ${error}`);
173
+ return null;
174
+ }
175
+ }
176
+
177
+ // Find floor value from bidder requests
178
+ function findFloorValueFromBidderRequests(auction, code) {
179
+ if (!auction?.bidderRequests?.length) return 0;
180
+
181
+ // Find all bids in bidder requests for this ad unit
182
+ const bidsFromRequests = auction.bidderRequests
183
+ .flatMap(request => request.bids || [])
184
+ .filter(bid => bid.adUnitCode === code);
185
+
186
+ if (!bidsFromRequests.length) {
187
+ logInfo(CONSTANTS.LOG_PRE_FIX, `No bids found for ad unit: ${code}`);
188
+ return 0;
189
+ }
190
+
191
+ const bidWithGetFloor = bidsFromRequests.find(bid => bid.getFloor);
192
+ if (!bidWithGetFloor) {
193
+ logInfo(CONSTANTS.LOG_PRE_FIX, `No bid with getFloor method found for ad unit: ${code}`);
194
+ return 0;
195
+ }
196
+
197
+ // Helper function to extract sizes with their media types from a source object
198
+ const extractSizes = (source) => {
199
+ if (!source) return null;
200
+
201
+ const result = [];
202
+
203
+ // Extract banner sizes
204
+ if (source.mediaTypes?.banner?.sizes) {
205
+ source.mediaTypes.banner.sizes.forEach(size => {
206
+ result.push({
207
+ size,
208
+ mediaType: 'banner'
209
+ });
210
+ });
211
+ }
212
+
213
+ // Extract video sizes
214
+ if (source.mediaTypes?.video?.playerSize) {
215
+ const playerSize = source.mediaTypes.video.playerSize;
216
+ // Handle both formats: [[w, h]] and [w, h]
217
+ const videoSizes = Array.isArray(playerSize[0]) ? playerSize : [playerSize];
218
+
219
+ videoSizes.forEach(size => {
220
+ result.push({
221
+ size,
222
+ mediaType: 'video'
223
+ });
224
+ });
225
+ }
226
+
227
+ // Use general sizes as fallback if no specific media types found
228
+ if (result.length === 0 && source.sizes) {
229
+ source.sizes.forEach(size => {
230
+ result.push({
231
+ size,
232
+ mediaType: 'banner' // Default to banner for general sizes
233
+ });
234
+ });
235
+ }
236
+
237
+ return result.length > 0 ? result : null;
238
+ };
239
+
240
+ // Try to get sizes from different sources in order of preference
241
+ const adUnit = auction.adUnits?.find(unit => unit.code === code);
242
+ let sizes = extractSizes(adUnit) || extractSizes(bidWithGetFloor);
243
+
244
+ // Handle fallback to wildcard size if no sizes found
245
+ if (!sizes) {
246
+ sizes = [{ size: ['*', '*'], mediaType: 'banner' }];
247
+ logInfo(CONSTANTS.LOG_PRE_FIX, `No sizes found, using wildcard size for ad unit: ${code}`);
248
+ }
249
+
250
+ // Try to get floor values for each size
251
+ let minFloor = -1;
252
+
253
+ for (const sizeObj of sizes) {
254
+ // Extract size and mediaType from the object
255
+ const { size, mediaType } = sizeObj;
256
+
257
+ // Call getFloor with the appropriate media type
258
+ const floorInfo = bidWithGetFloor.getFloor({
259
+ currency: 'USD', // Default currency
260
+ mediaType: mediaType, // Use the media type we extracted
261
+ size: size
262
+ });
263
+
264
+ if (floorInfo?.floor && !isNaN(parseFloat(floorInfo.floor))) {
265
+ const floorValue = parseFloat(floorInfo.floor);
266
+ logInfo(CONSTANTS.LOG_PRE_FIX, `Floor value for ${mediaType} size ${size}: ${floorValue}`);
267
+
268
+ // Update minimum floor value
269
+ minFloor = minFloor === -1 ? floorValue : Math.min(minFloor, floorValue);
270
+ }
271
+ }
272
+
273
+ if (minFloor !== -1) {
274
+ logInfo(CONSTANTS.LOG_PRE_FIX, `Calculated minimum floor value ${minFloor} for ad unit: ${code}`);
275
+ return minFloor;
276
+ }
277
+
278
+ logInfo(CONSTANTS.LOG_PRE_FIX, `No floor data found for ad unit: ${code}`);
279
+ return 0;
280
+ }
281
+
282
+ // Select multiplier based on priority order: floors.json → config.json → default
283
+ function selectMultiplier(multiplierKey, profileConfigs) {
284
+ // Define sources in priority order
285
+ const multiplierSources = [
286
+ {
287
+ name: 'config.json',
288
+ getValue: () => {
289
+ const configPath = profileConfigs?.plugins?.dynamicFloors?.pmTargetingKeys?.multiplier;
290
+ const lowerKey = multiplierKey.toLowerCase();
291
+ return configPath && lowerKey in configPath ? configPath[lowerKey] : null;
292
+ }
293
+ },
294
+ {
295
+ name: 'floor.json',
296
+ getValue: () => {
297
+ const configPath = profileConfigs?.plugins?.dynamicFloors?.data?.multiplier;
298
+ const lowerKey = multiplierKey.toLowerCase();
299
+ return configPath && lowerKey in configPath ? configPath[lowerKey] : null;
300
+ }
301
+ },
302
+ {
303
+ name: 'default',
304
+ getValue: () => CONSTANTS.MULTIPLIERS[multiplierKey]
305
+ }
306
+ ];
307
+
308
+ // Find the first source with a non-null value
309
+ for (const source of multiplierSources) {
310
+ const value = source.getValue();
311
+ if (value != null) {
312
+ return { value, source: source.name };
313
+ }
314
+ }
315
+
316
+ // Fallback (shouldn't happen due to default source)
317
+ return { value: CONSTANTS.MULTIPLIERS[multiplierKey], source: 'default' };
318
+ }
319
+
320
+ // Identify winning bid scenario and return scenario data
321
+ function handleWinningBidScenario(winningBid, code) {
322
+ return {
323
+ scenario: 'winning',
324
+ bidStatus: CONSTANTS.BID_STATUS.WON,
325
+ baseValue: winningBid.cpm,
326
+ multiplierKey: 'WIN',
327
+ logMessage: `Bid won for ad unit: ${code}, CPM: ${winningBid.cpm}`
328
+ };
329
+ }
330
+
331
+ // Identify rejected floor bid scenario and return scenario data
332
+ function handleRejectedFloorBidScenario(rejectedFloorBid, code) {
333
+ const baseValue = rejectedFloorBid.floorData?.floorValue || 0;
334
+ return {
335
+ scenario: 'rejected',
336
+ bidStatus: CONSTANTS.BID_STATUS.FLOORED,
337
+ baseValue,
338
+ multiplierKey: 'FLOORED',
339
+ logMessage: `Bid rejected due to price floor for ad unit: ${code}, Floor value: ${baseValue}, Bid CPM: ${rejectedFloorBid.cpm}`
340
+ };
341
+ }
342
+
343
+ // Identify no bid scenario and return scenario data
344
+ function handleNoBidScenario(auction, code) {
345
+ const baseValue = findFloorValueFromBidderRequests(auction, code);
346
+ return {
347
+ scenario: 'nobid',
348
+ bidStatus: CONSTANTS.BID_STATUS.NOBID,
349
+ baseValue,
350
+ multiplierKey: 'NOBID',
351
+ logMessage: `No bids for ad unit: ${code}, Floor value: ${baseValue}`
352
+ };
353
+ }
354
+
355
+ // Determine which scenario applies based on bid conditions
356
+ function determineScenario(winningBid, rejectedFloorBid, bidsForAdUnit, auction, code) {
357
+ return winningBid ? handleWinningBidScenario(winningBid, code)
358
+ : rejectedFloorBid ? handleRejectedFloorBidScenario(rejectedFloorBid, code)
359
+ : handleNoBidScenario(auction, code);
360
+ }
361
+
362
+ // Main function that determines bid status and calculates values
363
+ function determineBidStatusAndValues(winningBid, rejectedFloorBid, bidsForAdUnit, auction, code) {
364
+ const profileConfigs = getProfileConfigs();
365
+
366
+ // Determine the scenario based on bid conditions
367
+ const { bidStatus, baseValue, multiplierKey, logMessage } =
368
+ determineScenario(winningBid, rejectedFloorBid, bidsForAdUnit, auction, code);
369
+
370
+ // Select the appropriate multiplier
371
+ const { value: multiplier, source } = selectMultiplier(multiplierKey, profileConfigs);
372
+ logInfo(CONSTANTS.LOG_PRE_FIX, logMessage + ` (Using ${source} multiplier: ${multiplier})`);
373
+
374
+ return { bidStatus, baseValue, multiplier };
375
+ }