prebid.js 9.53.2 → 9.53.3

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 (196) 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/adagioRtdProvider.js +1 -1
  8. package/dist/adagioUtils.js +1 -1
  9. package/dist/addefendBidAdapter.js +1 -1
  10. package/dist/adgenerationBidAdapter.js +1 -1
  11. package/dist/adlooxRtdProvider.js +1 -1
  12. package/dist/adqueryBidAdapter.js +1 -1
  13. package/dist/adrelevantisBidAdapter.js +1 -1
  14. package/dist/adstirBidAdapter.js +1 -1
  15. package/dist/adtrgtmeBidAdapter.js +1 -1
  16. package/dist/adxcgAnalyticsAdapter.js +1 -1
  17. package/dist/adxcgBidAdapter.js +1 -1
  18. package/dist/adyoulikeBidAdapter.js +1 -1
  19. package/dist/agmaAnalyticsAdapter.js +1 -1
  20. package/dist/ajaBidAdapter.js +1 -1
  21. package/dist/amxBidAdapter.js +1 -1
  22. package/dist/amxIdSystem.js +1 -1
  23. package/dist/aniviewBidAdapter.js +1 -1
  24. package/dist/appierAnalyticsAdapter.js +1 -1
  25. package/dist/appnexusBidAdapter.js +1 -1
  26. package/dist/asoBidAdapter.js +1 -1
  27. package/dist/axonixBidAdapter.js +1 -1
  28. package/dist/beopBidAdapter.js +1 -1
  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/debugging-standalone.js +1 -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/gmosspBidAdapter.js +1 -1
  62. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  63. package/dist/greenbidsBidAdapter.js +1 -1
  64. package/dist/greenbidsRtdProvider.js +1 -1
  65. package/dist/gridBidAdapter.js +1 -1
  66. package/dist/gumgumBidAdapter.js +1 -1
  67. package/dist/h12mediaBidAdapter.js +1 -1
  68. package/dist/hypelabBidAdapter.js +1 -1
  69. package/dist/id5AnalyticsAdapter.js +1 -1
  70. package/dist/id5IdSystem.js +1 -1
  71. package/dist/imdsBidAdapter.js +1 -1
  72. package/dist/improvedigitalBidAdapter.js +1 -1
  73. package/dist/inmobiBidAdapter.js +1 -1
  74. package/dist/insticatorBidAdapter.js +1 -1
  75. package/dist/intentIqAnalyticsAdapter.js +1 -1
  76. package/dist/ixBidAdapter.js +1 -1
  77. package/dist/jixieBidAdapter.js +1 -1
  78. package/dist/justpremiumBidAdapter.js +1 -1
  79. package/dist/kargoBidAdapter.js +1 -1
  80. package/dist/kimberliteBidAdapter.js +1 -1
  81. package/dist/konduitAnalyticsAdapter.js +1 -1
  82. package/dist/kueezBidAdapter.js +1 -1
  83. package/dist/lassoBidAdapter.js +1 -1
  84. package/dist/lifestreetBidAdapter.js +1 -1
  85. package/dist/liveIntentId.js +1 -1
  86. package/dist/logicadBidAdapter.js +1 -1
  87. package/dist/loglyliftBidAdapter.js +1 -1
  88. package/dist/luceadBidAdapter.js +1 -1
  89. package/dist/mabidderBidAdapter.js +1 -1
  90. package/dist/madsenseBidAdapter.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/medianetBidAdapter.js +1 -1
  96. package/dist/medianetUtils.js +1 -1
  97. package/dist/mediasquareBidAdapter.js +1 -1
  98. package/dist/mgidBidAdapter.js +1 -1
  99. package/dist/missenaBidAdapter.js +1 -1
  100. package/dist/mobilefuseBidAdapter.js +1 -1
  101. package/dist/nextMillenniumBidAdapter.js +1 -1
  102. package/dist/nexx360Utils.js +1 -1
  103. package/dist/nobidAnalyticsAdapter.js +1 -1
  104. package/dist/nobidBidAdapter.js +1 -1
  105. package/dist/nodalsAiRtdProvider.js +1 -1
  106. package/dist/not-for-prod/prebid.js +172 -172
  107. package/dist/objectGuard.js +1 -1
  108. package/dist/oguryBidAdapter.js +1 -1
  109. package/dist/onetagBidAdapter.js +1 -1
  110. package/dist/ooloAnalyticsAdapter.js +1 -1
  111. package/dist/openxBidAdapter.js +1 -1
  112. package/dist/optidigitalBidAdapter.js +1 -1
  113. package/dist/orbidderBidAdapter.js +1 -1
  114. package/dist/outbrainBidAdapter.js +1 -1
  115. package/dist/pixfutureBidAdapter.js +1 -1
  116. package/dist/publinkIdSystem.js +1 -1
  117. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  118. package/dist/pubmaticBidAdapter.js +1 -1
  119. package/dist/pubmaticIdSystem.js +1 -1
  120. package/dist/pubmaticRtdProvider.js +1 -1
  121. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  122. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  123. package/dist/pxyzBidAdapter.js +1 -1
  124. package/dist/quantcastBidAdapter.js +1 -1
  125. package/dist/readpeakBidAdapter.js +1 -1
  126. package/dist/relaidoBidAdapter.js +1 -1
  127. package/dist/retailspotBidAdapter.js +1 -1
  128. package/dist/rhythmoneBidAdapter.js +1 -1
  129. package/dist/riseUtils.js +1 -1
  130. package/dist/rtdModule.js +1 -1
  131. package/dist/rubiconBidAdapter.js +1 -1
  132. package/dist/seedingAllianceBidAdapter.js +1 -1
  133. package/dist/seedtagBidAdapter.js +1 -1
  134. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  135. package/dist/sharethroughBidAdapter.js +1 -1
  136. package/dist/showheroes-bsBidAdapter.js +1 -1
  137. package/dist/smaatoBidAdapter.js +1 -1
  138. package/dist/smartadserverBidAdapter.js +1 -1
  139. package/dist/smartxBidAdapter.js +1 -1
  140. package/dist/smilewantedBidAdapter.js +1 -1
  141. package/dist/snigelBidAdapter.js +1 -1
  142. package/dist/sonobiBidAdapter.js +1 -1
  143. package/dist/sovrnBidAdapter.js +1 -1
  144. package/dist/sparteoBidAdapter.js +1 -1
  145. package/dist/sspBCBidAdapter.js +1 -1
  146. package/dist/stvBidAdapter.js +1 -1
  147. package/dist/sublimeBidAdapter.js +1 -1
  148. package/dist/taboolaBidAdapter.js +1 -1
  149. package/dist/tappxBidAdapter.js +1 -1
  150. package/dist/targetVideoBidAdapter.js +1 -1
  151. package/dist/teadsBidAdapter.js +1 -1
  152. package/dist/terceptAnalyticsAdapter.js +1 -1
  153. package/dist/themoneytizerBidAdapter.js +1 -1
  154. package/dist/trionBidAdapter.js +1 -1
  155. package/dist/tripleliftBidAdapter.js +1 -1
  156. package/dist/ttdBidAdapter.js +1 -1
  157. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  158. package/dist/uid2IdSystem.js +1 -1
  159. package/dist/underdogmediaBidAdapter.js +1 -1
  160. package/dist/undertoneBidAdapter.js +1 -1
  161. package/dist/unrulyBidAdapter.js +1 -1
  162. package/dist/userId.js +1 -1
  163. package/dist/vidazooUtils.js +1 -1
  164. package/dist/videobyteBidAdapter.js +1 -1
  165. package/dist/visxBidAdapter.js +1 -1
  166. package/dist/vuukleBidAdapter.js +1 -1
  167. package/dist/widespaceBidAdapter.js +1 -1
  168. package/dist/winrBidAdapter.js +1 -1
  169. package/dist/yahooAdsBidAdapter.js +1 -1
  170. package/dist/yandexBidAdapter.js +1 -1
  171. package/dist/yieldmoBidAdapter.js +1 -1
  172. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  173. package/libraries/objectGuard/objectGuard.js +149 -48
  174. package/libraries/objectGuard/ortbGuard.js +33 -43
  175. package/modules/adagioAnalyticsAdapter.js +6 -1
  176. package/modules/adagioBidAdapter.js +12 -5
  177. package/modules/adagioRtdProvider.js +41 -35
  178. package/modules/pubmaticAnalyticsAdapter.js +311 -587
  179. package/modules/pubmaticBidAdapter.js +71 -8
  180. package/modules/pubmaticIdSystem.js +4 -4
  181. package/modules/pubmaticRtdProvider.js +12 -60
  182. package/modules/rtdModule/index.js +1 -5
  183. package/modules/ttdBidAdapter.js +0 -5
  184. package/modules/userId/eids.js +1 -1
  185. package/modules/userId/index.js +32 -1
  186. package/package.json +1 -1
  187. package/src/auction.js +3 -0
  188. package/test/spec/activities/objectGuard_spec.js +156 -32
  189. package/test/spec/activities/ortbGuard_spec.js +10 -15
  190. package/test/spec/modules/adagioAnalyticsAdapter_spec.js +94 -24
  191. package/test/spec/modules/adagioRtdProvider_spec.js +17 -17
  192. package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +634 -916
  193. package/test/spec/modules/pubmaticBidAdapter_spec.js +260 -1
  194. package/test/spec/modules/pubmaticRtdProvider_spec.js +78 -400
  195. package/test/spec/modules/ttdBidAdapter_spec.js +0 -33
  196. package/test/spec/modules/userId_spec.js +115 -1
@@ -1,5 +1,5 @@
1
- import {isData, objectTransformer, sessionedApplies} from '../../src/activities/redactor.js';
2
- import {deepAccess, deepClone, deepEqual, deepSetValue} from '../../src/utils.js';
1
+ import {isData, sessionedApplies} from '../../src/activities/redactor.js';
2
+ import {deepEqual, logWarn} from '../../src/utils.js';
3
3
 
4
4
  /**
5
5
  * @typedef {import('../src/activities/redactor.js').TransformationRuleDef} TransformationRuleDef
@@ -12,66 +12,182 @@ import {deepAccess, deepClone, deepEqual, deepSetValue} from '../../src/utils.js
12
12
  /**
13
13
  * Create a factory function for object guards using the given rules.
14
14
  *
15
- * An object guard is a pair {obj, verify} where:
16
- * - `obj` is a view on the guarded object that applies "redact" rules (the same rules used in activites/redactor.js)
17
- * - `verify` is a function that, when called, will check that the guarded object was not modified
18
- * in a way that violates any "write protect" rules, and rolls back any offending changes.
15
+ * An object guard is a view on the guarded object that applies "redact" rules (the same rules used in activites/redactor.js),
16
+ * and prevents writes (including deltes) that violate "write protect" rules.
19
17
  *
20
18
  * This is meant to provide sandboxed version of a privacy-sensitive object, where reads
21
19
  * are filtered through redaction rules and writes are checked against write protect rules.
22
20
  *
23
- * @param {Array[TransformationRule]} rules
24
- * @return {function(*, ...[*]): ObjectGuard}
25
21
  */
26
22
  export function objectGuard(rules) {
27
23
  const root = {};
28
- const writeRules = [];
24
+
25
+ // rules are associated with specific portions of the object, e.g. "user.eids"
26
+ // build a tree representation of them, where the root is the object itself,
27
+ // and each node's children are properties of the corresponding (nested) object.
29
28
 
30
29
  rules.forEach(rule => {
31
- if (rule.wp) writeRules.push(rule);
32
- if (!rule.get) return;
33
30
  rule.paths.forEach(path => {
34
31
  let node = root;
35
32
  path.split('.').forEach(el => {
36
- node.children = node.children || {};
37
- node.children[el] = node.children[el] || {};
33
+ node.children = node.children ?? {};
34
+ node.children[el] = node.children[el] ?? { parent: node, path: node.path ? `${node.path}.${el}` : el };
38
35
  node = node.children[el];
39
- })
40
- node.rule = rule;
36
+ node.wpRules = node.wpRules ?? [];
37
+ node.redactRules = node.redactRules ?? [];
38
+ });
39
+ (rule.wp ? node.wpRules : node.redactRules).push(rule);
40
+ if (rule.wp) {
41
+ // mark the whole path as write protected, so that write operations
42
+ // on parents do not need to walk down the tree
43
+ let parent = node;
44
+ while (parent && !parent.hasWP) {
45
+ parent.hasWP = true;
46
+ parent = parent.parent;
47
+ }
48
+ }
41
49
  });
42
50
  });
43
51
 
44
- const wpTransformer = objectTransformer(writeRules);
52
+ function getRedactRule(node) {
53
+ if (node.redactRule == null) {
54
+ node.redactRule = node.redactRules.length === 0 ? false : {
55
+ check: (applies) => node.redactRules.some(applies),
56
+ get(val) {
57
+ for (const rule of node.redactRules) {
58
+ val = rule.get(val);
59
+ if (!isData(val)) break;
60
+ }
61
+ return val;
62
+ }
63
+ }
64
+ }
65
+ return node.redactRule;
66
+ }
67
+
68
+ function getWPRule(node) {
69
+ if (node.wpRule == null) {
70
+ node.wpRule = node.wpRules.length === 0 ? false : {
71
+ check: (applies) => node.wpRules.some(applies),
72
+ }
73
+ }
74
+ return node.wpRule;
75
+ }
76
+
77
+ /**
78
+ * clean up `newValue` so that it doesn't violate any write protect rules
79
+ * when set onto the property represented by 'node'.
80
+ *
81
+ * This is done substituting (portions of) `curValue` when some rule is violated.
82
+ */
83
+ function cleanup(node, curValue, newValue, applies) {
84
+ if (
85
+ !node.hasWP ||
86
+ (!isData(curValue) && !isData(newValue)) ||
87
+ deepEqual(curValue, newValue)
88
+ ) {
89
+ return newValue;
90
+ }
91
+ const rule = getWPRule(node);
92
+ if (rule && rule.check(applies)) {
93
+ return curValue;
94
+ }
95
+ if (node.children) {
96
+ for (const [prop, child] of Object.entries(node.children)) {
97
+ const propValue = cleanup(child, curValue?.[prop], newValue?.[prop], applies);
98
+ if (newValue != null && typeof newValue === 'object') {
99
+ if (!isData(propValue) && !curValue?.hasOwnProperty(prop)) {
100
+ delete newValue[prop];
101
+ } else {
102
+ newValue[prop] = propValue;
103
+ }
104
+ } else {
105
+ logWarn(`Invalid value set for '${node.path}', expected an object`, newValue);
106
+ return curValue;
107
+ }
108
+ }
109
+ }
110
+ return newValue;
111
+ }
45
112
 
46
- function mkGuard(obj, tree, applies) {
113
+ function isDeleteAllowed(node, curValue, applies) {
114
+ if (!node.hasWP || !isData(curValue)) {
115
+ return true;
116
+ }
117
+ const rule = getWPRule(node);
118
+ if (rule && rule.check(applies)) {
119
+ return false;
120
+ }
121
+ if (node.children) {
122
+ for (const [prop, child] of Object.entries(node.children)) {
123
+ if (!isDeleteAllowed(child, curValue?.[prop], applies)) {
124
+ return false;
125
+ }
126
+ }
127
+ }
128
+ return true;
129
+ }
130
+
131
+ function mkGuard(obj, tree, final, applies) {
47
132
  return new Proxy(obj, {
48
133
  get(target, prop, receiver) {
49
134
  const val = Reflect.get(target, prop, receiver);
50
- if (tree.hasOwnProperty(prop)) {
51
- const {children, rule} = tree[prop];
52
- if (children && val != null && typeof val === 'object') {
53
- return mkGuard(val, children, applies);
54
- } else if (rule && isData(val) && applies(rule)) {
55
- return rule.get(val);
135
+ if (final && val != null && typeof val === 'object') {
136
+ // a parent property has write protect rules, keep guarding
137
+ return mkGuard(val, tree, final, applies)
138
+ } else if (tree.children?.hasOwnProperty(prop)) {
139
+ const {children, hasWP} = tree.children[prop];
140
+ if ((children || hasWP) && val != null && typeof val === 'object') {
141
+ // some nested properties have rules, return a guard for the branch
142
+ return mkGuard(val, tree.children?.[prop] || tree, final || children == null, applies);
143
+ } else if (isData(val)) {
144
+ // if this property has redact rules, apply them
145
+ const rule = getRedactRule(tree.children[prop]);
146
+ if (rule && rule.check(applies)) {
147
+ return rule.get(val);
148
+ }
56
149
  }
57
150
  }
58
151
  return val;
59
152
  },
153
+ set(target, prop, newValue, receiver) {
154
+ if (final) {
155
+ // a parent property has rules, apply them
156
+ const rule = getWPRule(tree);
157
+ if (rule && rule.check(applies)) {
158
+ return true;
159
+ }
160
+ }
161
+ if (tree.children?.hasOwnProperty(prop)) {
162
+ // apply all (possibly nested) write protect rules
163
+ const curValue = Reflect.get(target, prop, receiver);
164
+ newValue = cleanup(tree.children[prop], curValue, newValue, applies);
165
+ if (!isData(newValue) && !target.hasOwnProperty(prop)) {
166
+ return true;
167
+ }
168
+ }
169
+ return Reflect.set(target, prop, newValue, receiver);
170
+ },
171
+ deleteProperty(target, prop) {
172
+ if (final) {
173
+ // a parent property has rules, apply them
174
+ const rule = getWPRule(tree);
175
+ if (rule && rule.check(applies)) {
176
+ return true;
177
+ }
178
+ }
179
+ if (tree.children?.hasOwnProperty(prop) && !isDeleteAllowed(tree.children[prop], target[prop], applies)) {
180
+ // some nested properties should not be deleted
181
+ return true;
182
+ }
183
+ return Reflect.deleteProperty(target, prop);
184
+ }
60
185
  });
61
186
  }
62
187
 
63
- function mkVerify(transformResult) {
64
- return function () {
65
- transformResult.forEach(fn => fn());
66
- }
67
- }
68
-
69
188
  return function guard(obj, ...args) {
70
189
  const session = {};
71
- return {
72
- obj: mkGuard(obj, root.children || {}, sessionedApplies(session, ...args)),
73
- verify: mkVerify(wpTransformer(session, obj, ...args))
74
- }
190
+ return mkGuard(obj, root, false, sessionedApplies(session, ...args))
75
191
  };
76
192
  }
77
193
 
@@ -82,20 +198,5 @@ export function objectGuard(rules) {
82
198
  export function writeProtectRule(ruleDef) {
83
199
  return Object.assign({
84
200
  wp: true,
85
- run(root, path, object, property, applies) {
86
- const origHasProp = object && object.hasOwnProperty(property);
87
- const original = origHasProp ? object[property] : undefined;
88
- const origCopy = origHasProp && original != null && typeof original === 'object' ? deepClone(original) : original;
89
- return function () {
90
- const object = path == null ? root : deepAccess(root, path);
91
- const finalHasProp = object && isData(object[property]);
92
- const finalValue = finalHasProp ? object[property] : undefined;
93
- if (!origHasProp && finalHasProp && applies()) {
94
- delete object[property];
95
- } else if ((origHasProp !== finalHasProp || finalValue !== original || !deepEqual(finalValue, origCopy)) && applies()) {
96
- deepSetValue(root, (path == null ? [] : [path]).concat(property).join('.'), origCopy);
97
- }
98
- }
99
- }
100
201
  }, ruleDef)
101
202
  }
@@ -7,11 +7,7 @@ import {
7
7
  ORTB_UFPD_PATHS
8
8
  } from '../../src/activities/redactor.js';
9
9
  import {objectGuard, writeProtectRule} from './objectGuard.js';
10
- import {mergeDeep} from '../../src/utils.js';
11
-
12
- /**
13
- * @typedef {import('./objectGuard.js').ObjectGuard} ObjectGuard
14
- */
10
+ import {logError} from '../../src/utils.js';
15
11
 
16
12
  function ortb2EnrichRules(isAllowed = isActivityAllowed) {
17
13
  return [
@@ -32,20 +28,10 @@ export function ortb2GuardFactory(isAllowed = isActivityAllowed) {
32
28
  return objectGuard(ortb2TransmitRules(isAllowed).concat(ortb2EnrichRules(isAllowed)));
33
29
  }
34
30
 
35
- /**
36
- *
37
- *
38
- * @typedef {Function} ortb2Guard
39
- * @param {{}} ortb2 ORTB object to guard
40
- * @param {{}} params activity params to use for activity checks
41
- * @returns {ObjectGuard}
42
- */
43
-
44
31
  /*
45
32
  * Get a guard for an ORTB object. Read access is restricted in the same way it'd be redacted (see activites/redactor.js);
46
33
  * and writes are checked against the enrich* activites.
47
34
  *
48
- * @type ortb2Guard
49
35
  */
50
36
  export const ortb2Guard = ortb2GuardFactory();
51
37
 
@@ -53,40 +39,44 @@ export function ortb2FragmentsGuardFactory(guardOrtb2 = ortb2Guard) {
53
39
  return function guardOrtb2Fragments(fragments, params) {
54
40
  fragments.global = fragments.global || {};
55
41
  fragments.bidder = fragments.bidder || {};
56
- const bidders = new Set(Object.keys(fragments.bidder));
57
- const verifiers = [];
58
-
59
- function makeGuard(ortb2) {
60
- const guard = guardOrtb2(ortb2, params);
61
- verifiers.push(guard.verify);
62
- return guard.obj;
63
- }
64
-
65
- const obj = {
66
- global: makeGuard(fragments.global),
67
- bidder: Object.fromEntries(Object.entries(fragments.bidder).map(([bidder, ortb2]) => [bidder, makeGuard(ortb2)]))
42
+ const guard = {
43
+ global: guardOrtb2(fragments.global, params),
44
+ bidder: new Proxy(fragments.bidder, {
45
+ get(target, prop, receiver) {
46
+ let bidderData = Reflect.get(target, prop, receiver);
47
+ if (bidderData != null) {
48
+ bidderData = guardOrtb2(bidderData, params)
49
+ }
50
+ return bidderData;
51
+ },
52
+ set(target, prop, newValue, receiver) {
53
+ if (newValue == null || typeof newValue !== 'object') {
54
+ logError(`ortb2Fragments.bidder[bidderCode] must be an object`);
55
+ }
56
+ let bidderData = Reflect.get(target, prop, receiver);
57
+ if (bidderData == null) {
58
+ bidderData = target[prop] = {};
59
+ }
60
+ bidderData = guardOrtb2(bidderData, params);
61
+ Object.entries(newValue).forEach(([prop, value]) => {
62
+ bidderData[prop] = value;
63
+ })
64
+ return true;
65
+ }
66
+ })
68
67
  };
69
68
 
70
- return {
71
- obj,
72
- verify() {
73
- Object.entries(obj.bidder)
74
- .filter(([bidder]) => !bidders.has(bidder))
75
- .forEach(([bidder, ortb2]) => {
76
- const repl = {};
77
- const guard = guardOrtb2(repl, params);
78
- mergeDeep(guard.obj, ortb2);
79
- guard.verify();
80
- fragments.bidder[bidder] = repl;
81
- })
82
- verifiers.forEach(fn => fn());
83
- }
84
- }
69
+ return Object.defineProperties(
70
+ {},
71
+ Object.fromEntries(
72
+ // disallow overwriting of the top level `global` / `bidder`
73
+ Object.entries(guard).map(([prop, obj]) => [prop, {get: () => obj}])
74
+ )
75
+ )
85
76
  }
86
77
  }
87
78
 
88
79
  /**
89
80
  * Get a guard for an ortb2Fragments object.
90
- * @type {function(*, *): ObjectGuard}
91
81
  */
92
82
  export const guardOrtb2Fragments = ortb2FragmentsGuardFactory();
@@ -267,7 +267,7 @@ function handlerAuctionInit(event) {
267
267
  ban_szs: bannerSizes.join(','),
268
268
  bdrs: sortedBidderNames.join(','),
269
269
  pgtyp: deepAccess(event.bidderRequests[0], 'ortb2.site.ext.data.pagetype', null),
270
- plcmt: deepAccess(adUnits[0], 'ortb2Imp.ext.data.placement', null),
270
+ plcmt: deepAccess(adUnits[0], 'ortb2Imp.ext.data.adg_rtd.placement', null), // adg_rtd.placement is set by AdagioRtdProvider.
271
271
  t_n: adgRtdSession.testName || null,
272
272
  t_v: adgRtdSession.testVersion || null,
273
273
  s_id: adgRtdSession.id || null,
@@ -289,6 +289,11 @@ function handlerAuctionInit(event) {
289
289
  // for backward compatibility: if we didn't find organizationId & site but we have a bid from adagio we might still find it in params
290
290
  qp.org_id = qp.org_id || adagioAdUnitBids[0].params.organizationId;
291
291
  qp.site = qp.site || adagioAdUnitBids[0].params.site;
292
+
293
+ // `qp.plcmt` uses the value set by the AdagioRtdProvider. If not present, we fallback on the value set at the adUnit.params level.
294
+ if (!qp.plcmt) {
295
+ qp.plcmt = deepAccess(adagioAdUnitBids[0], 'params.placement', null);
296
+ }
292
297
  }
293
298
  }
294
299
 
@@ -399,11 +399,18 @@ function autoFillParams(bid) {
399
399
  bid.params.site = adgGlobalConf.siteId.split(':')[1];
400
400
  }
401
401
 
402
- // `useAdUnitCodeAsPlacement` is an edge case. Useful when a Prebid Manager cannot handle properly params setting.
403
- // In Prebid.js 9, `placement` should be defined in ortb2Imp and the `useAdUnitCodeAsPlacement` param should be removed
404
- bid.params.placement = deepAccess(bid, 'ortb2Imp.ext.data.placement', bid.params.placement);
405
- if (!bid.params.placement && (adgGlobalConf.useAdUnitCodeAsPlacement === true || bid.params.useAdUnitCodeAsPlacement === true)) {
406
- bid.params.placement = bid.adUnitCode;
402
+ if (!bid.params.placement) {
403
+ let p = deepAccess(bid, 'ortb2Imp.ext.data.adg_rtd.placement', '');
404
+ if (!p) {
405
+ // Use ortb2Imp.ext.data.placement for backward compatibility.
406
+ p = deepAccess(bid, 'ortb2Imp.ext.data.placement', '');
407
+ }
408
+
409
+ // `useAdUnitCodeAsPlacement` is an edge case. Useful when a Prebid Manager cannot handle properly params setting.
410
+ if (!p && bid.params.useAdUnitCodeAsPlacement === true) {
411
+ p = bid.adUnitCode;
412
+ }
413
+ bid.params.placement = p;
407
414
  }
408
415
 
409
416
  bid.params.adUnitElementId = deepAccess(bid, 'ortb2Imp.ext.data.divId', bid.params.adUnitElementId);
@@ -47,7 +47,7 @@ export const PLACEMENT_SOURCES = {
47
47
  };
48
48
  export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME });
49
49
 
50
- const { logError, logWarn } = prefixLog('AdagioRtdProvider:');
50
+ const { logError, logInfo, logWarn } = prefixLog('AdagioRtdProvider:');
51
51
 
52
52
  // Guard to avoid storing the same bid data several times.
53
53
  const guard = new Set();
@@ -238,6 +238,25 @@ export const _internal = {
238
238
  return value;
239
239
  }
240
240
  });
241
+ },
242
+
243
+ // Compute the placement from the legacy RTD config params or ortb2Imp.ext.data.placement key.
244
+ computePlacementFromLegacy: function(rtdConfig, adUnit) {
245
+ const placementSource = deepAccess(rtdConfig, 'params.placementSource', '');
246
+ let placementFromSource = '';
247
+
248
+ switch (placementSource.toLowerCase()) {
249
+ case PLACEMENT_SOURCES.ADUNITCODE:
250
+ placementFromSource = adUnit.code;
251
+ break;
252
+ case PLACEMENT_SOURCES.GPID:
253
+ placementFromSource = deepAccess(adUnit, 'ortb2Imp.ext.gpid')
254
+ break;
255
+ }
256
+
257
+ const placementLegacy = deepAccess(adUnit, 'ortb2Imp.ext.data.placement', '');
258
+
259
+ return placementFromSource || placementLegacy;
241
260
  }
242
261
  };
243
262
 
@@ -317,7 +336,6 @@ function onBidRequest(bidderRequest, config, _userConsent) {
317
336
  * @param {*} config
318
337
  */
319
338
  function onGetBidRequestData(bidReqConfig, callback, config) {
320
- const configParams = deepAccess(config, 'params', {});
321
339
  const { site: ortb2Site } = bidReqConfig.ortb2Fragments.global;
322
340
  const features = _internal.getFeatures().get();
323
341
  const ext = {
@@ -345,30 +363,11 @@ function onGetBidRequestData(bidReqConfig, callback, config) {
345
363
  const slotPosition = getSlotPosition(divId);
346
364
  deepSetValue(ortb2Imp, `ext.data.adg_rtd.adunit_position`, slotPosition);
347
365
 
348
- // It is expected that the publisher set a `adUnits[].ortb2Imp.ext.data.placement` value.
349
- // Btw, We allow fallback sources to programmatically set this value.
350
- // The source is defined in the `config.params.placementSource` and the possible values are `code` or `gpid`.
351
- // (Please note that this `placement` is not related to the oRTB video property.)
352
- if (!deepAccess(ortb2Imp, 'ext.data.placement')) {
353
- const { placementSource = '' } = configParams;
354
-
355
- switch (placementSource.toLowerCase()) {
356
- case PLACEMENT_SOURCES.ADUNITCODE:
357
- deepSetValue(ortb2Imp, 'ext.data.placement', adUnit.code);
358
- break;
359
- case PLACEMENT_SOURCES.GPID:
360
- deepSetValue(ortb2Imp, 'ext.data.placement', deepAccess(ortb2Imp, 'ext.gpid'));
361
- break;
362
- default:
363
- logWarn('`ortb2Imp.ext.data.placement` is missing and `params.definePlacement` is not set in the config.');
364
- }
365
- }
366
-
367
- // We expect that `pagetype`, `category`, `placement` are defined in FPD `ortb2.site.ext.data` and `adUnits[].ortb2Imp.ext.data` objects.
368
- // Btw, we have to ensure compatibility with publishers that use the "legacy" adagio params at the adUnit.params level.
369
366
  const adagioBid = adUnit.bids.find(bid => _internal.isAdagioBidder(bid.bidder));
370
367
  if (adagioBid) {
371
368
  // ortb2 level
369
+ // We expect that `pagetype`, `category` are defined in FPD `ortb2.site.ext.data` object.
370
+ // Btw, we still ensure compatibility with publishers that use the adagio params at the adUnit.params level.
372
371
  let mustWarnOrtb2 = false;
373
372
  if (!deepAccess(ortb2Site, 'ext.data.pagetype') && adagioBid.params.pagetype) {
374
373
  deepSetValue(ortb2Site, 'ext.data.pagetype', adagioBid.params.pagetype);
@@ -378,21 +377,28 @@ function onGetBidRequestData(bidReqConfig, callback, config) {
378
377
  deepSetValue(ortb2Site, 'ext.data.category', adagioBid.params.category);
379
378
  mustWarnOrtb2 = true;
380
379
  }
381
-
382
- // ortb2Imp level
383
- let mustWarnOrtb2Imp = false;
384
- if (!deepAccess(ortb2Imp, 'ext.data.placement')) {
385
- if (adagioBid.params.placement) {
386
- deepSetValue(ortb2Imp, 'ext.data.placement', adagioBid.params.placement);
387
- mustWarnOrtb2Imp = true;
388
- }
380
+ if (mustWarnOrtb2) {
381
+ logInfo('`pagetype` and/or `category` have been set in the FPD `ortb2.site.ext.data` object from `adUnits[].bids.adagio.params`.');
389
382
  }
390
383
 
391
- if (mustWarnOrtb2) {
392
- logWarn('`pagetype` and `category` must be defined in the FPD `ortb2.site.ext.data` object. Relying on `adUnits[].bids.adagio.params` is deprecated.');
384
+ // ortb2Imp level to handle legacy.
385
+ // The `placement` is finally set at the adUnit.params level (see https://github.com/prebid/Prebid.js/issues/12845)
386
+ // but we still need to set it at the ortb2Imp level for our internal use.
387
+ const placementParam = adagioBid.params.placement;
388
+ const adgRtdPlacement = deepAccess(ortb2Imp, 'ext.data.adg_rtd.placement', '');
389
+
390
+ if (placementParam) {
391
+ // Always overwrite the ortb2Imp value with the one from the adagio adUnit.params.placement if defined.
392
+ // This is the common case.
393
+ deepSetValue(ortb2Imp, 'ext.data.adg_rtd.placement', placementParam);
393
394
  }
394
- if (mustWarnOrtb2Imp) {
395
- logWarn('`placement` must be defined in the FPD `adUnits[].ortb2Imp.ext.data` object. Relying on `adUnits[].bids.adagio.params` is deprecated.');
395
+
396
+ if (!placementParam && !adgRtdPlacement) {
397
+ const p = _internal.computePlacementFromLegacy(config, adUnit);
398
+ if (p) {
399
+ deepSetValue(ortb2Imp, 'ext.data.adg_rtd.placement', p);
400
+ logWarn('`ortb2Imp.ext.data.adg_rtd.placement` has been set from a legacy source. Please set `bids[].adagio.params.placement` or `ortb2Imp.ext.data.adg_rtd.placement` value.');
401
+ }
396
402
  }
397
403
  }
398
404
  });