prebid.js 7.51.0 → 7.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/1plusXRtdProvider.js +1 -1
- package/dist/33acrossBidAdapter.js +1 -1
- package/dist/33acrossIdSystem.js +1 -1
- package/dist/adagioBidAdapter.js +1 -1
- package/dist/adbookpspBidAdapter.js +1 -1
- package/dist/adgenerationBidAdapter.js +1 -1
- package/dist/adkernelBidAdapter.js +1 -1
- package/dist/adqueryBidAdapter.js +1 -1
- package/dist/adrelevantisBidAdapter.js +1 -1
- package/dist/adriverIdSystem.js +1 -1
- package/dist/adtrgtmeBidAdapter.js +1 -1
- package/dist/adxcgBidAdapter.js +1 -1
- package/dist/adyoulikeBidAdapter.js +1 -1
- package/dist/airgridRtdProvider.js +1 -1
- package/dist/ajaBidAdapter.js +1 -1
- package/dist/allowActivities.js +1 -0
- package/dist/amxBidAdapter.js +1 -1
- package/dist/amxIdSystem.js +1 -1
- package/dist/appierAnalyticsAdapter.js +1 -1
- package/dist/appnexusBidAdapter.js +1 -1
- package/dist/asoBidAdapter.js +1 -1
- package/dist/axonixBidAdapter.js +1 -1
- package/dist/beopBidAdapter.js +1 -1
- package/dist/bidglassBidAdapter.js +1 -1
- package/dist/big-richmediaBidAdapter.js +1 -1
- package/dist/bridgewellBidAdapter.js +1 -1
- package/dist/brightMountainMediaBidAdapter.js +1 -1
- package/dist/carodaBidAdapter.js +1 -1
- package/dist/chtnwBidAdapter.js +1 -1
- package/dist/conceptxBidAdapter.js +1 -0
- package/dist/concertBidAdapter.js +1 -1
- package/dist/connectIdSystem.js +1 -1
- package/dist/connectadBidAdapter.js +1 -1
- package/dist/consumableBidAdapter.js +1 -1
- package/dist/conversantAnalyticsAdapter.js +1 -1
- package/dist/conversantBidAdapter.js +1 -1
- package/dist/craftBidAdapter.js +1 -1
- package/dist/criteoBidAdapter.js +1 -1
- package/dist/cwireBidAdapter.js +1 -1
- package/dist/dependencies.json +6 -0
- package/dist/dspxBidAdapter.js +1 -1
- package/dist/eplanningBidAdapter.js +1 -1
- package/dist/eskimiBidAdapter.js +1 -1
- package/dist/euidIdSystem.js +1 -0
- package/dist/feedadBidAdapter.js +1 -1
- package/dist/finativeBidAdapter.js +1 -1
- package/dist/freepassIdSystem.js +1 -0
- package/dist/freewheel-sspBidAdapter.js +1 -1
- package/dist/gdprEnforcement.js +1 -1
- package/dist/glimpseBidAdapter.js +1 -1
- package/dist/gmosspBidAdapter.js +1 -1
- package/dist/goldbachBidAdapter.js +1 -1
- package/dist/greenbidsAnalyticsAdapter.js +1 -1
- package/dist/greenbidsRtdProvider.js +1 -1
- package/dist/gridBidAdapter.js +1 -1
- package/dist/growthCodeRtdProvider.js +1 -0
- package/dist/gumgumBidAdapter.js +1 -1
- package/dist/h12mediaBidAdapter.js +1 -1
- package/dist/id5IdSystem.js +1 -1
- package/dist/improvedigitalBidAdapter.js +1 -1
- package/dist/inmarBidAdapter.js +1 -1
- package/dist/insticatorBidAdapter.js +1 -1
- package/dist/ixBidAdapter.js +1 -1
- package/dist/justpremiumBidAdapter.js +1 -1
- package/dist/kargoBidAdapter.js +1 -1
- package/dist/konduitAnalyticsAdapter.js +1 -1
- package/dist/kueezBidAdapter.js +1 -1
- package/dist/kueezRtbBidAdapter.js +1 -1
- package/dist/kulturemediaBidAdapter.js +1 -1
- package/dist/lassoBidAdapter.js +1 -1
- package/dist/lifestreetBidAdapter.js +1 -1
- package/dist/limelightDigitalBidAdapter.js +1 -1
- package/dist/liveyieldAnalyticsAdapter.js +1 -1
- package/dist/logicadBidAdapter.js +1 -1
- package/dist/loglyliftBidAdapter.js +1 -1
- package/dist/magniteAnalyticsAdapter.js +1 -1
- package/dist/malltvAnalyticsAdapter.js +1 -1
- package/dist/marsmediaBidAdapter.js +1 -1
- package/dist/mediafuseBidAdapter.js +1 -1
- package/dist/mediasquareBidAdapter.js +1 -1
- package/dist/mgidBidAdapter.js +1 -1
- package/dist/minutemediaBidAdapter.js +1 -1
- package/dist/minutemediaplusBidAdapter.js +1 -1
- package/dist/nexx360BidAdapter.js +1 -1
- package/dist/not-for-prod/prebid.js +154 -146
- package/dist/objectGuard.js +1 -0
- package/dist/oguryBidAdapter.js +1 -1
- package/dist/onetagBidAdapter.js +1 -1
- package/dist/ooloAnalyticsAdapter.js +1 -1
- package/dist/optidigitalBidAdapter.js +1 -1
- package/dist/outbrainBidAdapter.js +1 -1
- package/dist/oxxionAnalyticsAdapter.js +1 -0
- package/dist/pairIdSystem.js +1 -1
- package/dist/parrableIdSystem.js +1 -1
- package/dist/pixfutureBidAdapter.js +1 -1
- package/dist/prebid-core.js +1 -1
- package/dist/proxistoreBidAdapter.js +1 -1
- package/dist/publinkIdSystem.js +1 -1
- package/dist/pubmaticBidAdapter.js +1 -1
- package/dist/pubwiseAnalyticsAdapter.js +1 -1
- package/dist/pxyzBidAdapter.js +1 -1
- package/dist/quantcastBidAdapter.js +1 -1
- package/dist/readpeakBidAdapter.js +1 -1
- package/dist/relaidoBidAdapter.js +1 -1
- package/dist/retailspotBidAdapter.js +1 -1
- package/dist/rhythmoneBidAdapter.js +1 -1
- package/dist/richaudienceBidAdapter.js +1 -1
- package/dist/riseBidAdapter.js +1 -1
- package/dist/rtdModule.js +1 -1
- package/dist/rubiconAnalyticsAdapter.js +1 -1
- package/dist/rubiconBidAdapter.js +1 -1
- package/dist/schain.js +1 -1
- package/dist/seedingAllianceBidAdapter.js +1 -1
- package/dist/seedtagBidAdapter.js +1 -1
- package/dist/sharethroughAnalyticsAdapter.js +1 -1
- package/dist/sharethroughBidAdapter.js +1 -1
- package/dist/shinezBidAdapter.js +1 -1
- package/dist/sirdataRtdProvider.js +1 -1
- package/dist/smaatoBidAdapter.js +1 -1
- package/dist/smartadserverBidAdapter.js +1 -1
- package/dist/smartxBidAdapter.js +1 -1
- package/dist/smilewantedBidAdapter.js +1 -1
- package/dist/sonobiBidAdapter.js +1 -1
- package/dist/sovrnAnalyticsAdapter.js +1 -1
- package/dist/sovrnBidAdapter.js +1 -1
- package/dist/sspBCBidAdapter.js +1 -1
- package/dist/stroeerCoreBidAdapter.js +1 -1
- package/dist/stvBidAdapter.js +1 -1
- package/dist/sublimeBidAdapter.js +1 -1
- package/dist/synacormediaBidAdapter.js +1 -1
- package/dist/targetVideoBidAdapter.js +1 -1
- package/dist/teadsBidAdapter.js +1 -1
- package/dist/trionBidAdapter.js +1 -1
- package/dist/tripleliftBidAdapter.js +1 -1
- package/dist/ttdBidAdapter.js +1 -1
- package/dist/ucfunnelAnalyticsAdapter.js +1 -1
- package/dist/uid2IdSystem.js +1 -1
- package/dist/uid2IdSystem_shared.js +1 -0
- package/dist/underdogmediaBidAdapter.js +1 -1
- package/dist/undertoneBidAdapter.js +1 -1
- package/dist/userId.js +1 -1
- package/dist/vidazooBidAdapter.js +1 -1
- package/dist/videobyteBidAdapter.js +1 -1
- package/dist/viouslyBidAdapter.js +1 -1
- package/dist/visxBidAdapter.js +1 -1
- package/dist/vuukleBidAdapter.js +1 -1
- package/dist/weboramaRtdProvider.js +1 -1
- package/dist/widespaceBidAdapter.js +1 -1
- package/dist/winrBidAdapter.js +1 -1
- package/dist/yahoosspBidAdapter.js +1 -1
- package/dist/yieldlabBidAdapter.js +1 -1
- package/dist/yieldmoBidAdapter.js +1 -1
- package/dist/yieldoneAnalyticsAdapter.js +1 -1
- package/dist/zeta_global_sspBidAdapter.js +1 -1
- package/integrationExamples/gpt/growthcode.html +20 -9
- package/integrationExamples/gpt/userId_example.html +20 -6
- package/libraries/objectGuard/objectGuard.js +108 -0
- package/libraries/objectGuard/ortbGuard.js +88 -0
- package/modules/.submodules.json +3 -1
- package/modules/1plusXRtdProvider.js +31 -60
- package/modules/adagioBidAdapter.js +86 -24
- package/modules/adgenerationBidAdapter.js +14 -5
- package/modules/adkernelBidAdapter.js +2 -2
- package/modules/adriverIdSystem.js +1 -1
- package/modules/airgridRtdProvider.js +11 -12
- package/modules/allowActivities.js +74 -0
- package/modules/asoBidAdapter.js +2 -1
- package/modules/beopBidAdapter.js +6 -0
- package/modules/conceptxBidAdapter.js +70 -0
- package/modules/conceptxBidAdapter.md +36 -0
- package/modules/connectIdSystem.js +89 -13
- package/modules/connectIdSystem.md +4 -7
- package/modules/criteoBidAdapter.js +18 -2
- package/modules/eskimiBidAdapter.js +171 -41
- package/modules/eskimiBidAdapter.md +35 -16
- package/modules/euidIdSystem.js +121 -0
- package/modules/euidIdSystem.md +131 -0
- package/modules/freepassIdSystem.js +61 -0
- package/modules/freepassIdSystem.md +47 -0
- package/modules/gdprEnforcement.js +98 -169
- package/modules/greenbidsAnalyticsAdapter.js +1 -1
- package/modules/growthCodeRtdProvider.js +131 -0
- package/modules/growthCodeRtdProvider.md +55 -0
- package/modules/id5IdSystem.js +3 -5
- package/modules/ixBidAdapter.js +17 -3
- package/modules/limelightDigitalBidAdapter.js +1 -1
- package/modules/logicadBidAdapter.js +14 -1
- package/modules/mediasquareBidAdapter.js +13 -5
- package/modules/nexx360BidAdapter.js +1 -0
- package/modules/oxxionAnalyticsAdapter.js +212 -0
- package/modules/oxxionAnalyticsAdapter.md +33 -0
- package/modules/pairIdSystem.js +16 -11
- package/modules/proxistoreBidAdapter.js +11 -14
- package/modules/richaudienceBidAdapter.js +10 -1
- package/modules/riseBidAdapter.js +1 -1
- package/modules/rtdModule/index.js +12 -1
- package/modules/schain.js +2 -2
- package/modules/sharethroughBidAdapter.js +2 -2
- package/modules/sirdataRtdProvider.js +73 -146
- package/modules/smartadserverBidAdapter.js +5 -0
- package/modules/sovrnBidAdapter.js +4 -1
- package/modules/stroeerCoreBidAdapter.js +55 -6
- package/modules/stvBidAdapter.js +34 -1
- package/modules/uid2IdSystem.js +27 -210
- package/modules/uid2IdSystem.md +104 -23
- package/modules/uid2IdSystem_shared.js +232 -0
- package/modules/undertoneBidAdapter.js +9 -1
- package/modules/userId/eids.js +8 -0
- package/modules/userId/index.js +69 -41
- package/modules/userId/userId.md +2 -1
- package/modules/viouslyBidAdapter.js +2 -2
- package/modules/weboramaRtdProvider.js +25 -95
- package/modules/weboramaRtdProvider.md +4 -4
- package/modules/yahoosspBidAdapter.js +45 -3
- package/modules/yahoosspBidAdapter.md +1 -1
- package/modules/yieldlabBidAdapter.js +23 -14
- package/modules/zeta_global_sspBidAdapter.js +28 -22
- package/package.json +1 -1
- package/src/activities/activities.js +47 -0
- package/src/activities/activityParams.js +8 -0
- package/src/activities/modules.js +1 -1
- package/src/activities/params.js +59 -0
- package/src/activities/redactor.js +157 -0
- package/src/activities/rules.js +95 -0
- package/src/adapterManager.js +45 -8
- package/src/adloader.js +1 -0
- package/src/fpd/rootDomain.js +1 -1
- package/src/prebid.js +1 -1
- package/src/storageManager.js +57 -44
- package/src/userSync.js +35 -18
- package/test/spec/activities/allowActivites_spec.js +138 -0
- package/test/spec/activities/objectGuard_spec.js +144 -0
- package/test/spec/activities/ortbGuard_spec.js +140 -0
- package/test/spec/activities/params_spec.js +25 -0
- package/test/spec/activities/redactor_spec.js +296 -0
- package/test/spec/activities/rules_spec.js +135 -0
- package/test/spec/modules/1plusXRtdProvider_spec.js +98 -149
- package/test/spec/modules/adagioBidAdapter_spec.js +46 -17
- package/test/spec/modules/adgenerationBidAdapter_spec.js +52 -12
- package/test/spec/modules/airgridRtdProvider_spec.js +2 -0
- package/test/spec/modules/beopBidAdapter_spec.js +24 -0
- package/test/spec/modules/conceptxBidAdapter_spec.js +136 -0
- package/test/spec/modules/connectIdSystem_spec.js +291 -23
- package/test/spec/modules/criteoBidAdapter_spec.js +85 -1
- package/test/spec/modules/eids_spec.js +15 -0
- package/test/spec/modules/eskimiBidAdapter_spec.js +264 -112
- package/test/spec/modules/euidIdSystem_spec.js +130 -0
- package/test/spec/modules/freepassIdSystem_spec.js +186 -0
- package/test/spec/modules/gdprEnforcement_spec.js +127 -414
- package/test/spec/modules/growthCodeRtdProvider_spec.js +127 -0
- package/test/spec/modules/id5IdSystem_spec.js +10 -3
- package/test/spec/modules/ixBidAdapter_spec.js +32 -1
- package/test/spec/modules/logicadBidAdapter_spec.js +122 -2
- package/test/spec/modules/mediasquareBidAdapter_spec.js +4 -0
- package/test/spec/modules/oxxionAnalyticsAdapter_spec.js +324 -0
- package/test/spec/modules/pairIdSystem_spec.js +16 -3
- package/test/spec/modules/realTimeDataModule_spec.js +1 -1
- package/test/spec/modules/richaudienceBidAdapter_spec.js +44 -0
- package/test/spec/modules/schain_spec.js +9 -0
- package/test/spec/modules/sharethroughBidAdapter_spec.js +1 -1
- package/test/spec/modules/sirdataRtdProvider_spec.js +110 -17
- package/test/spec/modules/smartadserverBidAdapter_spec.js +42 -0
- package/test/spec/modules/sovrnBidAdapter_spec.js +13 -0
- package/test/spec/modules/stroeerCoreBidAdapter_spec.js +151 -6
- package/test/spec/modules/stvBidAdapter_spec.js +13 -1
- package/test/spec/modules/uid2IdSystem_helpers.js +70 -0
- package/test/spec/modules/uid2IdSystem_spec.js +131 -86
- package/test/spec/modules/undertoneBidAdapter_spec.js +57 -1
- package/test/spec/modules/userId_spec.js +96 -33
- package/test/spec/modules/weboramaRtdProvider_spec.js +331 -205
- package/test/spec/modules/yahoosspBidAdapter_spec.js +103 -51
- package/test/spec/modules/yieldlabBidAdapter_spec.js +42 -2
- package/test/spec/modules/zeta_global_sspBidAdapter_spec.js +204 -4
- package/test/spec/unit/core/adapterManager_spec.js +181 -1
- package/test/spec/unit/core/storageManager_spec.js +76 -68
- package/test/spec/unit/pbjs_api_spec.js +15 -25
- package/test/spec/userSync_spec.js +45 -16
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
export const Uid2CodeVersion = '1.1';
|
|
3
|
+
|
|
4
|
+
function isValidIdentity(identity) {
|
|
5
|
+
return !!(typeof identity === 'object' && identity !== null && identity.advertising_token && identity.identity_expires && identity.refresh_from && identity.refresh_token && identity.refresh_expires);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// This is extracted from an in-progress API client. Once it's available via NPM, this class should be replaced with the NPM package.
|
|
9
|
+
export class Uid2ApiClient {
|
|
10
|
+
constructor(opts, clientId, logInfo, logWarn) {
|
|
11
|
+
this._baseUrl = opts.baseUrl;
|
|
12
|
+
this._clientVersion = clientId;
|
|
13
|
+
this._logInfo = logInfo;
|
|
14
|
+
this._logWarn = logWarn;
|
|
15
|
+
}
|
|
16
|
+
createArrayBuffer(text) {
|
|
17
|
+
const arrayBuffer = new Uint8Array(text.length);
|
|
18
|
+
for (let i = 0; i < text.length; i++) {
|
|
19
|
+
arrayBuffer[i] = text.charCodeAt(i);
|
|
20
|
+
}
|
|
21
|
+
return arrayBuffer;
|
|
22
|
+
}
|
|
23
|
+
hasStatusResponse(response) {
|
|
24
|
+
return typeof (response) === 'object' && response && response.status;
|
|
25
|
+
}
|
|
26
|
+
isValidRefreshResponse(response) {
|
|
27
|
+
return this.hasStatusResponse(response) && (
|
|
28
|
+
response.status === 'optout' || response.status === 'expired_token' || (response.status === 'success' && response.body && isValidIdentity(response.body))
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
ResponseToRefreshResult(response) {
|
|
32
|
+
if (this.isValidRefreshResponse(response)) {
|
|
33
|
+
if (response.status === 'success') { return { status: response.status, identity: response.body }; }
|
|
34
|
+
return response;
|
|
35
|
+
} else { return `Response didn't contain a valid status`; }
|
|
36
|
+
}
|
|
37
|
+
callRefreshApi(refreshDetails) {
|
|
38
|
+
const url = this._baseUrl + '/v2/token/refresh';
|
|
39
|
+
const req = new XMLHttpRequest();
|
|
40
|
+
req.overrideMimeType('text/plain');
|
|
41
|
+
req.open('POST', url, true);
|
|
42
|
+
req.setRequestHeader('X-UID2-Client-Version', this._clientVersion);
|
|
43
|
+
let resolvePromise;
|
|
44
|
+
let rejectPromise;
|
|
45
|
+
const promise = new Promise((resolve, reject) => {
|
|
46
|
+
resolvePromise = resolve;
|
|
47
|
+
rejectPromise = reject;
|
|
48
|
+
});
|
|
49
|
+
req.onreadystatechange = () => {
|
|
50
|
+
if (req.readyState !== req.DONE) { return; }
|
|
51
|
+
try {
|
|
52
|
+
if (!refreshDetails.refresh_response_key || req.status !== 200) {
|
|
53
|
+
this._logInfo('Error status OR no response decryption key available, assuming unencrypted JSON');
|
|
54
|
+
const response = JSON.parse(req.responseText);
|
|
55
|
+
const result = this.ResponseToRefreshResult(response);
|
|
56
|
+
if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); }
|
|
57
|
+
} else {
|
|
58
|
+
this._logInfo('Decrypting refresh API response');
|
|
59
|
+
const encodeResp = this.createArrayBuffer(atob(req.responseText));
|
|
60
|
+
window.crypto.subtle.importKey('raw', this.createArrayBuffer(atob(refreshDetails.refresh_response_key)), { name: 'AES-GCM' }, false, ['decrypt']).then((key) => {
|
|
61
|
+
this._logInfo('Imported decryption key')
|
|
62
|
+
// returns the symmetric key
|
|
63
|
+
window.crypto.subtle.decrypt({
|
|
64
|
+
name: 'AES-GCM',
|
|
65
|
+
iv: encodeResp.slice(0, 12),
|
|
66
|
+
tagLength: 128, // The tagLength you used to encrypt (if any)
|
|
67
|
+
}, key, encodeResp.slice(12)).then((decrypted) => {
|
|
68
|
+
const decryptedResponse = String.fromCharCode(...new Uint8Array(decrypted));
|
|
69
|
+
this._logInfo('Decrypted to:', decryptedResponse);
|
|
70
|
+
const response = JSON.parse(decryptedResponse);
|
|
71
|
+
const result = this.ResponseToRefreshResult(response);
|
|
72
|
+
if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); }
|
|
73
|
+
}, (reason) => this._logWarn(`Call to UID2 API failed`, reason));
|
|
74
|
+
}, (reason) => this._logWarn(`Call to UID2 API failed`, reason));
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
rejectPromise(err);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
this._logInfo('Sending refresh request', refreshDetails);
|
|
81
|
+
req.send(refreshDetails.refresh_token);
|
|
82
|
+
return promise;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class Uid2StorageManager {
|
|
86
|
+
constructor(storage, preferLocalStorage, storageName, logInfo) {
|
|
87
|
+
this._storage = storage;
|
|
88
|
+
this._preferLocalStorage = preferLocalStorage;
|
|
89
|
+
this._storageName = storageName;
|
|
90
|
+
this._logInfo = logInfo;
|
|
91
|
+
}
|
|
92
|
+
readCookie(cookieName) {
|
|
93
|
+
return this._storage.cookiesAreEnabled() ? this._storage.getCookie(cookieName) : null;
|
|
94
|
+
}
|
|
95
|
+
readLocalStorage(key) {
|
|
96
|
+
return this._storage.localStorageIsEnabled() ? this._storage.getDataFromLocalStorage(key) : null;
|
|
97
|
+
}
|
|
98
|
+
readModuleCookie() {
|
|
99
|
+
return this.parseIfContainsBraces(this.readCookie(this._storageName));
|
|
100
|
+
}
|
|
101
|
+
writeModuleCookie(value) {
|
|
102
|
+
this._storage.setCookie(this._storageName, JSON.stringify(value), Date.now() + 60 * 60 * 24 * 1000);
|
|
103
|
+
}
|
|
104
|
+
readModuleStorage() {
|
|
105
|
+
return this.parseIfContainsBraces(this.readLocalStorage(this._storageName));
|
|
106
|
+
}
|
|
107
|
+
writeModuleStorage(value) {
|
|
108
|
+
this._storage.setDataInLocalStorage(this._storageName, JSON.stringify(value));
|
|
109
|
+
}
|
|
110
|
+
readProvidedCookie(cookieName) {
|
|
111
|
+
return JSON.parse(this.readCookie(cookieName));
|
|
112
|
+
}
|
|
113
|
+
parseIfContainsBraces(value) {
|
|
114
|
+
return (value?.includes('{')) ? JSON.parse(value) : value;
|
|
115
|
+
}
|
|
116
|
+
storeValue(value) {
|
|
117
|
+
if (this._preferLocalStorage) {
|
|
118
|
+
this.writeModuleStorage(value);
|
|
119
|
+
} else {
|
|
120
|
+
this.writeModuleCookie(value);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
getStoredValueWithFallback() {
|
|
125
|
+
const preferredStorageLabel = this._preferLocalStorage ? 'local storage' : 'cookie';
|
|
126
|
+
const preferredStorageGet = (this._preferLocalStorage ? this.readModuleStorage : this.readModuleCookie).bind(this);
|
|
127
|
+
const preferredStorageSet = (this._preferLocalStorage ? this.writeModuleStorage : this.writeModuleCookie).bind(this);
|
|
128
|
+
const fallbackStorageGet = (this._preferLocalStorage ? this.readModuleCookie : this.readModuleStorage).bind(this);
|
|
129
|
+
|
|
130
|
+
const storedValue = preferredStorageGet();
|
|
131
|
+
|
|
132
|
+
if (!storedValue) {
|
|
133
|
+
const fallbackValue = fallbackStorageGet();
|
|
134
|
+
if (fallbackValue) {
|
|
135
|
+
this._logInfo(`${preferredStorageLabel} was empty, but found a fallback value.`)
|
|
136
|
+
if (typeof fallbackValue === 'object') {
|
|
137
|
+
this._logInfo(`Copying the fallback value to ${preferredStorageLabel}.`);
|
|
138
|
+
preferredStorageSet(fallbackValue);
|
|
139
|
+
}
|
|
140
|
+
return fallbackValue;
|
|
141
|
+
}
|
|
142
|
+
} else if (typeof storedValue === 'string') {
|
|
143
|
+
const fallbackValue = fallbackStorageGet();
|
|
144
|
+
if (fallbackValue && typeof fallbackValue === 'object') {
|
|
145
|
+
this._logInfo(`${preferredStorageLabel} contained a basic token, but found a refreshable token fallback. Copying the fallback value to ${preferredStorageLabel}.`);
|
|
146
|
+
preferredStorageSet(fallbackValue);
|
|
147
|
+
return fallbackValue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return storedValue;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function refreshTokenAndStore(baseUrl, token, clientId, storageManager, _logInfo, _logWarn) {
|
|
155
|
+
_logInfo('UID2 base url provided: ', baseUrl);
|
|
156
|
+
const client = new Uid2ApiClient({baseUrl}, clientId, _logInfo, _logWarn);
|
|
157
|
+
return client.callRefreshApi(token).then((response) => {
|
|
158
|
+
_logInfo('Refresh endpoint responded with:', response);
|
|
159
|
+
const tokens = {
|
|
160
|
+
originalToken: token,
|
|
161
|
+
latestToken: response.identity,
|
|
162
|
+
};
|
|
163
|
+
storageManager.storeValue(tokens);
|
|
164
|
+
return tokens;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) {
|
|
169
|
+
let suppliedToken = null;
|
|
170
|
+
const preferLocalStorage = (config.storage !== 'cookie');
|
|
171
|
+
const storageManager = new Uid2StorageManager(prebidStorageManager, preferLocalStorage, config.internalStorage, _logInfo);
|
|
172
|
+
_logInfo(`Module is using ${preferLocalStorage ? 'local storage' : 'cookies'} for internal storage.`);
|
|
173
|
+
|
|
174
|
+
if (config.paramToken) {
|
|
175
|
+
suppliedToken = config.paramToken;
|
|
176
|
+
_logInfo('Read token from params', suppliedToken);
|
|
177
|
+
} else if (config.serverCookieName) {
|
|
178
|
+
suppliedToken = storageManager.readProvidedCookie(config.serverCookieName);
|
|
179
|
+
_logInfo('Read token from server-supplied cookie', suppliedToken);
|
|
180
|
+
}
|
|
181
|
+
let storedTokens = storageManager.getStoredValueWithFallback();
|
|
182
|
+
_logInfo('Loaded module-stored tokens:', storedTokens);
|
|
183
|
+
|
|
184
|
+
if (storedTokens && typeof storedTokens === 'string') {
|
|
185
|
+
// Stored value is a plain token - if no token is supplied, just use the stored value.
|
|
186
|
+
|
|
187
|
+
if (!suppliedToken) {
|
|
188
|
+
_logInfo('Returning legacy cookie value.');
|
|
189
|
+
return { id: storedTokens };
|
|
190
|
+
}
|
|
191
|
+
// Otherwise, ignore the legacy value - it should get over-written later anyway.
|
|
192
|
+
_logInfo('Discarding superseded legacy cookie.');
|
|
193
|
+
storedTokens = null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (suppliedToken && storedTokens) {
|
|
197
|
+
if (storedTokens.originalToken?.advertising_token !== suppliedToken.advertising_token) {
|
|
198
|
+
_logInfo('Server supplied new token - ignoring stored value.', storedTokens.originalToken?.advertising_token, suppliedToken.advertising_token);
|
|
199
|
+
// Stored token wasn't originally sourced from the provided token - ignore the stored value. A new user has logged in?
|
|
200
|
+
storedTokens = null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// At this point, any legacy values or superseded stored tokens have been nulled out.
|
|
204
|
+
const useSuppliedToken = !(storedTokens?.latestToken) || (suppliedToken && suppliedToken.identity_expires > storedTokens.latestToken.identity_expires);
|
|
205
|
+
const newestAvailableToken = useSuppliedToken ? suppliedToken : storedTokens.latestToken;
|
|
206
|
+
_logInfo('UID2 module selected latest token', useSuppliedToken, newestAvailableToken);
|
|
207
|
+
if (!newestAvailableToken || Date.now() > newestAvailableToken.refresh_expires) {
|
|
208
|
+
_logInfo('Newest available token is expired and not refreshable.');
|
|
209
|
+
return { id: null };
|
|
210
|
+
}
|
|
211
|
+
if (Date.now() > newestAvailableToken.identity_expires) {
|
|
212
|
+
const promise = refreshTokenAndStore(config.apiBaseUrl, newestAvailableToken, config.clientId, storageManager, _logInfo, _logWarn);
|
|
213
|
+
_logInfo('Token is expired but can be refreshed, attempting refresh.');
|
|
214
|
+
return { callback: (cb) => {
|
|
215
|
+
promise.then((result) => {
|
|
216
|
+
_logInfo('Refresh reponded, passing the updated token on.', result);
|
|
217
|
+
cb(result);
|
|
218
|
+
});
|
|
219
|
+
} };
|
|
220
|
+
}
|
|
221
|
+
// If should refresh (but don't need to), refresh in the background.
|
|
222
|
+
if (Date.now() > newestAvailableToken.refresh_from) {
|
|
223
|
+
_logInfo(`Refreshing token in background with low priority.`);
|
|
224
|
+
refreshTokenAndStore(config.apiBaseUrl, newestAvailableToken, config.clientId, storageManager, _logInfo, _logWarn);
|
|
225
|
+
}
|
|
226
|
+
const tokens = {
|
|
227
|
+
originalToken: suppliedToken ?? storedTokens?.originalToken,
|
|
228
|
+
latestToken: newestAvailableToken,
|
|
229
|
+
};
|
|
230
|
+
storageManager.storeValue(tokens);
|
|
231
|
+
return { id: tokens };
|
|
232
|
+
}
|
|
@@ -124,6 +124,12 @@ export const spec = {
|
|
|
124
124
|
reqUrl += `&ccpa=${bidderRequest.uspConsent}`;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
if (bidderRequest.gppConsent) {
|
|
128
|
+
const gppString = bidderRequest.gppConsent.gppString ?? '';
|
|
129
|
+
const ggpSid = bidderRequest.gppConsent.applicableSections ?? '';
|
|
130
|
+
reqUrl += `&gpp=${gppString}&gpp_sid=${ggpSid}`;
|
|
131
|
+
}
|
|
132
|
+
|
|
127
133
|
validBidRequests.map(bidReq => {
|
|
128
134
|
const bid = {
|
|
129
135
|
bidRequestId: bidReq.bidId,
|
|
@@ -146,7 +152,9 @@ export const spec = {
|
|
|
146
152
|
streamType: deepAccess(bidReq, 'mediaTypes.video.context') || null,
|
|
147
153
|
playbackMethod: deepAccess(bidReq, 'params.video.playbackMethod') || null,
|
|
148
154
|
maxDuration: deepAccess(bidReq, 'params.video.maxDuration') || null,
|
|
149
|
-
skippable: deepAccess(bidReq, 'params.video.skippable') || null
|
|
155
|
+
skippable: deepAccess(bidReq, 'params.video.skippable') || null,
|
|
156
|
+
placement: deepAccess(bidReq, 'mediaTypes.video.placement') || null,
|
|
157
|
+
plcmt: deepAccess(bidReq, 'mediaTypes.video.plcmt') || null
|
|
150
158
|
};
|
|
151
159
|
}
|
|
152
160
|
payload['x-ut-hb-params'].push(bid);
|
package/modules/userId/eids.js
CHANGED
package/modules/userId/index.js
CHANGED
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
* @property {SubmoduleConfig} config
|
|
111
111
|
* @property {(Object|undefined)} idObj - cache decoded id value (this is copied to every adUnit bid)
|
|
112
112
|
* @property {(function|undefined)} callback - holds reference to submodule.getId() result if it returned a function. Will be set to undefined after callback executes
|
|
113
|
+
* @property {StorageManager} storageMgr
|
|
113
114
|
*/
|
|
114
115
|
|
|
115
116
|
/**
|
|
@@ -133,7 +134,12 @@ import adapterManager, {gdprDataHandler} from '../../src/adapterManager.js';
|
|
|
133
134
|
import CONSTANTS from '../../src/constants.json';
|
|
134
135
|
import {hook, module, ready as hooksReady} from '../../src/hook.js';
|
|
135
136
|
import {buildEidPermissions, createEidsArray, USER_IDS_CONFIG} from './eids.js';
|
|
136
|
-
import {
|
|
137
|
+
import {
|
|
138
|
+
getCoreStorageManager,
|
|
139
|
+
getStorageManager,
|
|
140
|
+
STORAGE_TYPE_COOKIES,
|
|
141
|
+
STORAGE_TYPE_LOCALSTORAGE
|
|
142
|
+
} from '../../src/storageManager.js';
|
|
137
143
|
import {
|
|
138
144
|
cyrb53Hash,
|
|
139
145
|
deepAccess,
|
|
@@ -159,6 +165,9 @@ import {newMetrics, timedAuctionHook, useMetrics} from '../../src/utils/perfMetr
|
|
|
159
165
|
import {findRootDomain} from '../../src/fpd/rootDomain.js';
|
|
160
166
|
import {GDPR_GVLIDS} from '../../src/consentHandler.js';
|
|
161
167
|
import {MODULE_TYPE_UID} from '../../src/activities/modules.js';
|
|
168
|
+
import {isActivityAllowed} from '../../src/activities/rules.js';
|
|
169
|
+
import {ACTIVITY_ENRICH_EIDS} from '../../src/activities/activities.js';
|
|
170
|
+
import {activityParams} from '../../src/activities/activityParams.js';
|
|
162
171
|
|
|
163
172
|
const MODULE_NAME = 'User ID';
|
|
164
173
|
const COOKIE = STORAGE_TYPE_COOKIES;
|
|
@@ -170,7 +179,10 @@ const CONSENT_DATA_COOKIE_STORAGE_CONFIG = {
|
|
|
170
179
|
expires: 30 // 30 days expiration, which should match how often consent is refreshed by CMPs
|
|
171
180
|
};
|
|
172
181
|
export const PBJS_USER_ID_OPTOUT_NAME = '_pbjs_id_optout';
|
|
173
|
-
export const coreStorage = getCoreStorageManager('
|
|
182
|
+
export const coreStorage = getCoreStorageManager('userId');
|
|
183
|
+
export const dep = {
|
|
184
|
+
isAllowed: isActivityAllowed
|
|
185
|
+
}
|
|
174
186
|
|
|
175
187
|
/** @type {boolean} */
|
|
176
188
|
let addedUserIdHook = false;
|
|
@@ -220,11 +232,12 @@ export function setSubmoduleRegistry(submodules) {
|
|
|
220
232
|
submoduleRegistry = submodules;
|
|
221
233
|
}
|
|
222
234
|
|
|
223
|
-
function cookieSetter(submodule) {
|
|
235
|
+
function cookieSetter(submodule, storageMgr) {
|
|
236
|
+
storageMgr = storageMgr || submodule.storageMgr;
|
|
224
237
|
const domainOverride = (typeof submodule.submodule.domainOverride === 'function') ? submodule.submodule.domainOverride() : null;
|
|
225
238
|
const name = submodule.config.storage.name;
|
|
226
239
|
return function setCookie(suffix, value, expiration) {
|
|
227
|
-
|
|
240
|
+
storageMgr.setCookie(name + (suffix || ''), value, expiration, 'Lax', domainOverride);
|
|
228
241
|
}
|
|
229
242
|
}
|
|
230
243
|
|
|
@@ -237,6 +250,7 @@ export function setStoredValue(submodule, value) {
|
|
|
237
250
|
* @type {SubmoduleStorage}
|
|
238
251
|
*/
|
|
239
252
|
const storage = submodule.config.storage;
|
|
253
|
+
const mgr = submodule.storageMgr;
|
|
240
254
|
|
|
241
255
|
try {
|
|
242
256
|
const expiresStr = (new Date(Date.now() + (storage.expires * (60 * 60 * 24 * 1000)))).toUTCString();
|
|
@@ -248,10 +262,10 @@ export function setStoredValue(submodule, value) {
|
|
|
248
262
|
setCookie('_last', new Date().toUTCString(), expiresStr);
|
|
249
263
|
}
|
|
250
264
|
} else if (storage.type === LOCAL_STORAGE) {
|
|
251
|
-
|
|
252
|
-
|
|
265
|
+
mgr.setDataInLocalStorage(`${storage.name}_exp`, expiresStr);
|
|
266
|
+
mgr.setDataInLocalStorage(storage.name, encodeURIComponent(valueStr));
|
|
253
267
|
if (typeof storage.refreshInSeconds === 'number') {
|
|
254
|
-
|
|
268
|
+
mgr.setDataInLocalStorage(`${storage.name}_last`, new Date().toUTCString());
|
|
255
269
|
}
|
|
256
270
|
}
|
|
257
271
|
} catch (error) {
|
|
@@ -263,7 +277,7 @@ export function deleteStoredValue(submodule) {
|
|
|
263
277
|
let deleter, suffixes;
|
|
264
278
|
switch (submodule.config?.storage?.type) {
|
|
265
279
|
case COOKIE:
|
|
266
|
-
const setCookie = cookieSetter(submodule);
|
|
280
|
+
const setCookie = cookieSetter(submodule, coreStorage);
|
|
267
281
|
const expiry = (new Date(Date.now() - 1000 * 60 * 60 * 24)).toUTCString();
|
|
268
282
|
deleter = (suffix) => setCookie(suffix, '', expiry)
|
|
269
283
|
suffixes = ['', '_last'];
|
|
@@ -292,25 +306,26 @@ function setPrebidServerEidPermissions(initializedSubmodules) {
|
|
|
292
306
|
}
|
|
293
307
|
|
|
294
308
|
/**
|
|
295
|
-
|
|
296
|
-
* @param {SubmoduleStorage} storage
|
|
309
|
+
* @param {SubmoduleContainer} submodule
|
|
297
310
|
* @param {String|undefined} key optional key of the value
|
|
298
311
|
* @returns {string}
|
|
299
312
|
*/
|
|
300
|
-
function getStoredValue(
|
|
313
|
+
function getStoredValue(submodule, key = undefined) {
|
|
314
|
+
const mgr = submodule.storageMgr;
|
|
315
|
+
const storage = submodule.config.storage;
|
|
301
316
|
const storedKey = key ? `${storage.name}_${key}` : storage.name;
|
|
302
317
|
let storedValue;
|
|
303
318
|
try {
|
|
304
319
|
if (storage.type === COOKIE) {
|
|
305
|
-
storedValue =
|
|
320
|
+
storedValue = mgr.getCookie(storedKey);
|
|
306
321
|
} else if (storage.type === LOCAL_STORAGE) {
|
|
307
|
-
const storedValueExp =
|
|
322
|
+
const storedValueExp = mgr.getDataFromLocalStorage(`${storage.name}_exp`);
|
|
308
323
|
// empty string means no expiration set
|
|
309
324
|
if (storedValueExp === '') {
|
|
310
|
-
storedValue =
|
|
325
|
+
storedValue = mgr.getDataFromLocalStorage(storedKey);
|
|
311
326
|
} else if (storedValueExp) {
|
|
312
327
|
if ((new Date(storedValueExp)).getTime() - Date.now() > 0) {
|
|
313
|
-
storedValue = decodeURIComponent(
|
|
328
|
+
storedValue = decodeURIComponent(mgr.getDataFromLocalStorage(storedKey));
|
|
314
329
|
}
|
|
315
330
|
}
|
|
316
331
|
}
|
|
@@ -415,7 +430,7 @@ function processSubmoduleCallbacks(submodules, cb) {
|
|
|
415
430
|
moduleDone();
|
|
416
431
|
}
|
|
417
432
|
try {
|
|
418
|
-
submodule.callback(callbackCompleted, getStoredValue.bind(null, submodule
|
|
433
|
+
submodule.callback(callbackCompleted, getStoredValue.bind(null, submodule));
|
|
419
434
|
} catch (e) {
|
|
420
435
|
logError(`Error in userID module '${submodule.submodule.name}':`, e);
|
|
421
436
|
moduleDone();
|
|
@@ -773,6 +788,8 @@ function getUserIdsAsync() {
|
|
|
773
788
|
* This hook returns updated list of submodules which are allowed to do get user id based on TCF 2 enforcement rules configured
|
|
774
789
|
*/
|
|
775
790
|
export const validateGdprEnforcement = hook('sync', function (submodules, consentData) {
|
|
791
|
+
// TODO: remove the `hasValidated` check in v8. Enforcement should be OFF by default.
|
|
792
|
+
// https://github.com/prebid/Prebid.js/issues/9766
|
|
776
793
|
return { userIdModules: submodules, hasValidated: consentData && consentData.hasValidated };
|
|
777
794
|
}, 'validateGdprEnforcement');
|
|
778
795
|
|
|
@@ -781,12 +798,12 @@ function populateSubmoduleId(submodule, consentData, storedConsentData, forceRef
|
|
|
781
798
|
// 1. storage: retrieve user id data from cookie/html storage or with the submodule's getId method
|
|
782
799
|
// 2. value: pass directly to bids
|
|
783
800
|
if (submodule.config.storage) {
|
|
784
|
-
let storedId = getStoredValue(submodule
|
|
801
|
+
let storedId = getStoredValue(submodule);
|
|
785
802
|
let response;
|
|
786
803
|
|
|
787
804
|
let refreshNeeded = false;
|
|
788
805
|
if (typeof submodule.config.storage.refreshInSeconds === 'number') {
|
|
789
|
-
const storedDate = new Date(getStoredValue(submodule
|
|
806
|
+
const storedDate = new Date(getStoredValue(submodule, 'last'));
|
|
790
807
|
refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > submodule.config.storage.refreshInSeconds * 1000);
|
|
791
808
|
}
|
|
792
809
|
|
|
@@ -849,17 +866,23 @@ function initSubmodules(dest, submodules, consentData, forceRefresh = false) {
|
|
|
849
866
|
return uidMetrics().fork().measureTime('userId.init.modules', function () {
|
|
850
867
|
if (!submodules.length) return []; // to simplify log messages from here on
|
|
851
868
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
869
|
+
/**
|
|
870
|
+
* filter out submodules that:
|
|
871
|
+
*
|
|
872
|
+
* - cannot use the storage they've been set up with (storage not available / not allowed / disabled)
|
|
873
|
+
* - are not allowed to perform the `enrichEids` activity
|
|
874
|
+
*/
|
|
875
|
+
submodules = submodules.filter((submod) => {
|
|
876
|
+
return (!submod.config.storage || canUseStorage(submod)) &&
|
|
877
|
+
dep.isAllowed(ACTIVITY_ENRICH_EIDS, activityParams(MODULE_TYPE_UID, submod.config.name));
|
|
878
|
+
});
|
|
856
879
|
|
|
857
880
|
if (!submodules.length) {
|
|
858
|
-
logWarn(`${MODULE_NAME} - no ID module
|
|
881
|
+
logWarn(`${MODULE_NAME} - no ID module configured`);
|
|
859
882
|
return [];
|
|
860
883
|
}
|
|
861
884
|
|
|
862
|
-
//
|
|
885
|
+
// TODO: remove this check in v8 (https://github.com/prebid/Prebid.js/issues/9766)
|
|
863
886
|
let { userIdModules, hasValidated } = validateGdprEnforcement(submodules, consentData);
|
|
864
887
|
if (!hasValidated && !hasPurpose1Consent(consentData)) {
|
|
865
888
|
logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`);
|
|
@@ -940,24 +963,28 @@ function getValidSubmoduleConfigs(configRegistry, submoduleRegistry) {
|
|
|
940
963
|
|
|
941
964
|
const ALL_STORAGE_TYPES = new Set([LOCAL_STORAGE, COOKIE]);
|
|
942
965
|
|
|
943
|
-
function
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
966
|
+
function canUseStorage(submodule) {
|
|
967
|
+
switch (submodule.config?.storage?.type) {
|
|
968
|
+
case LOCAL_STORAGE:
|
|
969
|
+
if (submodule.storageMgr.localStorageIsEnabled()) {
|
|
970
|
+
if (coreStorage.getDataFromLocalStorage(PBJS_USER_ID_OPTOUT_NAME)) {
|
|
971
|
+
logInfo(`${MODULE_NAME} - opt-out localStorage found, storage disabled`);
|
|
972
|
+
return false
|
|
973
|
+
}
|
|
974
|
+
return true;
|
|
975
|
+
}
|
|
976
|
+
break;
|
|
977
|
+
case COOKIE:
|
|
978
|
+
if (submodule.storageMgr.cookiesAreEnabled()) {
|
|
979
|
+
if (coreStorage.getCookie(PBJS_USER_ID_OPTOUT_NAME)) {
|
|
980
|
+
logInfo(`${MODULE_NAME} - opt-out cookie found, storage disabled`);
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
return true
|
|
984
|
+
}
|
|
985
|
+
break;
|
|
959
986
|
}
|
|
960
|
-
return
|
|
987
|
+
return false;
|
|
961
988
|
}
|
|
962
989
|
|
|
963
990
|
/**
|
|
@@ -985,6 +1012,7 @@ function updateSubmodules() {
|
|
|
985
1012
|
config: submoduleConfig,
|
|
986
1013
|
callback: undefined,
|
|
987
1014
|
idObj: undefined,
|
|
1015
|
+
storageMgr: getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: submoduleConfig.name}),
|
|
988
1016
|
} : null;
|
|
989
1017
|
}).filter(submodule => submodule !== null)
|
|
990
1018
|
.forEach((sm) => submodules.push(sm));
|
package/modules/userId/userId.md
CHANGED
|
@@ -5,7 +5,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js';
|
|
|
5
5
|
import find from 'core-js-pure/features/array/find.js'; // eslint-disable-line prebid/validate-imports
|
|
6
6
|
|
|
7
7
|
const BIDDER_CODE = 'viously';
|
|
8
|
-
|
|
8
|
+
const GVLID = 1028;
|
|
9
9
|
const CURRENCY = 'EUR';
|
|
10
10
|
const TTL = 60;
|
|
11
11
|
const HTTP_METHOD = 'POST';
|
|
@@ -15,7 +15,7 @@ const REQUIRED_VIOUSLY_PARAMS = ['pid'];
|
|
|
15
15
|
|
|
16
16
|
export const spec = {
|
|
17
17
|
code: BIDDER_CODE,
|
|
18
|
-
|
|
18
|
+
gvlid: GVLID,
|
|
19
19
|
supportedMediaTypes: [BANNER, VIDEO],
|
|
20
20
|
|
|
21
21
|
/**
|