From 72ce4ec978724b907795f3dbc0c4bb79de0c689a Mon Sep 17 00:00:00 2001 From: ako-adot <90328748+ako-adot@users.noreply.github.com> Date: Thu, 7 Jul 2022 16:39:29 +0200 Subject: [PATCH 001/569] ADOT Bid Adapter: fix handle empty refererInfo (#8643) * [DSP-3707] fix: Handle empty refererInfo [DSP-3707] fix: Handle empty refererInfo * [DSP-3707] fix: handle empty referer info --- modules/adotBidAdapter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 75dcef02b59..869cd69a830 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -49,15 +49,15 @@ function tryParse(data) { * @returns {Site|null} Formatted Site OpenRtb object or null */ function getOpenRTBSiteObject(bidderRequest) { - if (!bidderRequest || !bidderRequest.refererInfo) return null; + const refererInfo = (bidderRequest && bidderRequest.refererInfo) || null; - const domain = bidderRequest.refererInfo.domain; + const domain = refererInfo ? refererInfo.domain : window.location.hostname; const publisherId = config.getConfig('adot.publisherId'); if (!domain) return null; return { - page: bidderRequest.refererInfo.page, + page: refererInfo ? refererInfo.page : window.location.href, domain: domain, name: domain, publisher: { From 84eca1f2d53ef0dbadf5582ac81db91f146af423 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 7 Jul 2022 15:06:12 +0000 Subject: [PATCH 002/569] Prebid 7.5.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95e0fb76c78..b464b7d819a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.5.0-pre", + "version": "7.5.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index c4a83e22774..c58827099ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.5.0-pre", + "version": "7.5.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 2009048160f94611d0d5440d74857d3e0dd3ad6b Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 7 Jul 2022 15:06:13 +0000 Subject: [PATCH 003/569] Increment version to 7.6.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b464b7d819a..622fff0792f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.5.0", + "version": "7.6.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index c58827099ab..1a8e1ff1a98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.5.0", + "version": "7.6.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 1cb69142eba80853f37ef661da23d411846eb443 Mon Sep 17 00:00:00 2001 From: mamatic <52153441+mamatic@users.noreply.github.com> Date: Thu, 7 Jul 2022 20:00:39 +0200 Subject: [PATCH 004/569] ATS-analytics-adapter - remove envelope source from check, should we fire analytics request (#8656) --- modules/atsAnalyticsAdapter.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index f45d2e80055..390e06366d1 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -267,7 +267,7 @@ function sendDataToAnalytic (events) { } // preflight request, to check did publisher have permission to send data to analytics endpoint -function preflightRequest (envelopeSourceCookieValue, events) { +function preflightRequest (events) { logInfo('ATS Analytics - preflight request!'); ajax(preflightUrl + atsAnalyticsAdapter.context.pid, { @@ -277,7 +277,7 @@ function preflightRequest (envelopeSourceCookieValue, events) { let samplingRate = samplingRateObject.samplingRate; atsAnalyticsAdapter.setSamplingCookie(samplingRate); let samplingRateNumber = Number(samplingRate); - if (data && samplingRate && atsAnalyticsAdapter.shouldFireRequest(samplingRateNumber) && envelopeSourceCookieValue != null) { + if (data && samplingRate && atsAnalyticsAdapter.shouldFireRequest(samplingRateNumber)) { logInfo('ATS Analytics - events to send: ', events); sendDataToAnalytic(events); } @@ -377,12 +377,11 @@ atsAnalyticsAdapter.callHandler = function (evtype, args) { } // check should we send data to analytics or not, check first cookie value _lr_sampling_rate try { - let envelopeSourceCookieValue = storage.getCookie('_lr_env_src_ats'); let samplingRateCookie = storage.getCookie('_lr_sampling_rate'); if (!samplingRateCookie) { - preflightRequest(envelopeSourceCookieValue, events); + preflightRequest(events); } else { - if (atsAnalyticsAdapter.shouldFireRequest(parseInt(samplingRateCookie)) && envelopeSourceCookieValue != null) { + if (atsAnalyticsAdapter.shouldFireRequest(parseInt(samplingRateCookie))) { logInfo('ATS Analytics - events to send: ', events); sendDataToAnalytic(events); } From a29cafd7d0c4a50b3d8529065c92ef1b45f82cfa Mon Sep 17 00:00:00 2001 From: eknis Date: Fri, 8 Jul 2022 04:56:11 +0900 Subject: [PATCH 005/569] imRtdProvider: update ix bidderDefaultFunction (#8657) * remove ix bidder setting * update test --- modules/imRtdProvider.js | 8 -------- test/spec/modules/imRtdProvider_spec.js | 1 - 2 files changed, 9 deletions(-) diff --git a/modules/imRtdProvider.js b/modules/imRtdProvider.js index b33e4407b13..0ed239019cf 100644 --- a/modules/imRtdProvider.js +++ b/modules/imRtdProvider.js @@ -42,14 +42,6 @@ function setImDataInCookie(value) { */ export function getBidderFunction(bidderName) { const biddersFunction = { - ix: function (bid, data) { - if (data.im_segments && data.im_segments.length) { - config.setConfig({ - ix: {firstPartyData: {im_segments: data.im_segments}}, - }); - } - return bid - }, pubmatic: function (bid, data) { if (data.im_segments && data.im_segments.length) { const dctr = deepAccess(bid, 'params.dctr'); diff --git a/test/spec/modules/imRtdProvider_spec.js b/test/spec/modules/imRtdProvider_spec.js index 6d92440a144..b580e5b6516 100644 --- a/test/spec/modules/imRtdProvider_spec.js +++ b/test/spec/modules/imRtdProvider_spec.js @@ -50,7 +50,6 @@ describe('imRtdProvider', function () { describe('getBidderFunction', function () { const assumedBidder = [ - 'ix', 'pubmatic', 'fluct' ]; From be0e69bd968e55f96bde6f7467b12d56b7d529a8 Mon Sep 17 00:00:00 2001 From: shahinrahbariasl <56240400+shahinrahbariasl@users.noreply.github.com> Date: Fri, 8 Jul 2022 14:47:10 -0400 Subject: [PATCH 006/569] IX Bid Adapter: Add support for impression level transactionId (imp[].ext.tid) (#8641) * IX Bid Adapter: add imp ext tid support * fix tid level in imp ext object Co-authored-by: shahin.rahbariasl --- modules/ixBidAdapter.js | 6 +++- test/spec/modules/ixBidAdapter_spec.js | 41 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 3076dbb6436..c22c0af7f4d 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -1050,6 +1050,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { const gpid = impressions[transactionIds[adUnitIndex]].gpid; const dfpAdUnitCode = impressions[transactionIds[adUnitIndex]].dfp_ad_unit_code; + const tid = impressions[transactionIds[adUnitIndex]].tid if (impressionObjects.length && BANNER in impressionObjects[0]) { const { id, banner: { topframe } } = impressionObjects[0]; const _bannerImpression = { @@ -1060,10 +1061,11 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { }, } - if (dfpAdUnitCode || gpid) { + if (dfpAdUnitCode || gpid || tid) { _bannerImpression.ext = {}; _bannerImpression.ext.dfp_ad_unit_code = dfpAdUnitCode; _bannerImpression.ext.gpid = gpid; + _bannerImpression.ext.tid = tid; } if ('bidfloor' in impressionObjects[0]) { @@ -1272,6 +1274,7 @@ function createVideoImps(validBidRequest, videoImps) { videoImps[validBidRequest.transactionId].ixImps = []; videoImps[validBidRequest.transactionId].ixImps.push(imp); videoImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); + videoImps[validBidRequest.transactionId].tid = deepAccess(validBidRequest, 'ortb2Imp.ext.tid'); } } @@ -1298,6 +1301,7 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { bannerImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); bannerImps[validBidRequest.transactionId].dfp_ad_unit_code = deepAccess(validBidRequest, 'ortb2Imp.ext.data.adserver.adslot'); + bannerImps[validBidRequest.transactionId].tid = deepAccess(validBidRequest, 'ortb2Imp.ext.tid'); // Create IX imps from params.size if (bannerSizeDefined) { diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index a79b73d34e2..233fac6f426 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -121,6 +121,11 @@ describe('IndexexchangeAdapter', function () { playerSize: [[400, 100]] } }, + ortb2Imp: { + ext: { + tid: '173f49a8-7549-4218-a23c-e7ba59b47230' + } + }, adUnitCode: 'div-gpt-ad-1460505748562-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', @@ -143,6 +148,11 @@ describe('IndexexchangeAdapter', function () { sizes: [[300, 250]] } }, + ortb2Imp: { + ext: { + tid: '173f49a8-7549-4218-a23c-e7ba59b47229' + } + }, adUnitCode: 'div-gpt-ad-1460505748561-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47229', bidId: '1a2b3c4d', @@ -165,6 +175,11 @@ describe('IndexexchangeAdapter', function () { sizes: [[300, 250], [300, 600]] } }, + ortb2Imp: { + ext: { + tid: '173f49a8-7549-4218-a23c-e7ba59b47229' + } + }, adUnitCode: 'div-gpt-ad-1460505748561-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47229', bidId: '1a2b3c4d', @@ -185,6 +200,11 @@ describe('IndexexchangeAdapter', function () { sizes: [[300, 250], [300, 600]] } }, + ortb2Imp: { + ext: { + tid: '173f49a8-7549-4218-a23c-e7ba59b47229' + } + }, adUnitCode: 'div-gpt-ad-1460505748561-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47229', bidId: '1a2b3c4d', @@ -214,6 +234,11 @@ describe('IndexexchangeAdapter', function () { protocols: [2] } }, + ortb2Imp: { + ext: { + tid: '173f49a8-7549-4218-a23c-e7ba59b47230' + } + }, adUnitCode: 'div-gpt-ad-1460505748562-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', @@ -247,6 +272,11 @@ describe('IndexexchangeAdapter', function () { playerSize: [[400, 100], [200, 400]] } }, + ortb2Imp: { + ext: { + tid: '173f49a8-7549-4218-a23c-e7ba59b47230' + } + }, adUnitCode: 'div-gpt-ad-1460505748562-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', @@ -272,6 +302,11 @@ describe('IndexexchangeAdapter', function () { sizes: [[300, 250], [300, 600], [400, 500]] } }, + ortb2Imp: { + ext: { + tid: '173f49a8-7549-4218-a23c-e7ba59b47230' + } + }, adUnitCode: 'div-gpt-ad-1460505748562-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', @@ -307,6 +342,11 @@ describe('IndexexchangeAdapter', function () { sizes: [[300, 250], [300, 600]] } }, + ortb2Imp: { + ext: { + tid: '173f49a8-7549-4218-a23c-e7ba59b47230' + } + }, adUnitCode: 'div-gpt-ad-1460505748562-0', transactionId: '273f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', @@ -1720,6 +1760,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); expect(impression.banner.format).to.be.length(2); expect(impression.banner.topframe).to.be.oneOf([0, 1]); + expect(impression.ext.tid).to.equal(DEFAULT_BANNER_VALID_BID[0].transactionId); impression.banner.format.map(({ w, h, ext }, index) => { const size = DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[index]; From 0e039881a27a803179fda00904c4d67ef0689c0a Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 11 Jul 2022 05:20:24 -0700 Subject: [PATCH 007/569] PBjs Core Price Floors Module: improve logging on bid rejections to clarify which CPM is being compared with which floor (#8655) * Price floors module: improve logging on bid rejections to clarify which CPM is being compared with which floor * Use full precision in log message --- modules/priceFloors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index f397e92055e..900276e6b4e 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -751,7 +751,7 @@ export function addBidResponseHook(fn, adUnitCode, bid) { flooredBid.status = CONSTANTS.BID_STATUS.BID_REJECTED; // if floor not met update bid with 0 cpm so it is not included downstream and marked as no-bid flooredBid.cpm = 0; - logWarn(`${MODULE_NAME}: ${flooredBid.bidderCode}'s Bid Response for ${adUnitCode} was rejected due to floor not met`, bid); + logWarn(`${MODULE_NAME}: ${flooredBid.bidderCode}'s Bid Response for ${adUnitCode} was rejected due to floor not met (adjusted cpm: ${bid?.floorData?.cpmAfterAdjustments}, floor: ${floorInfo?.matchingFloor})`, bid); return fn.call(this, adUnitCode, flooredBid); } return fn.call(this, adUnitCode, bid); From cae46d65fa78ce5b53af799f0ccbef4882a40cc7 Mon Sep 17 00:00:00 2001 From: Keisuke Kakinuma Date: Mon, 11 Jul 2022 21:41:59 +0900 Subject: [PATCH 008/569] Adgeneration Bid Adpter: add Criteo system and ID5 system and update test. (#8642) * update Adgeneration to add Criteo system and ID5 systems. * Change method name. * Correction_of_code_and_test_specs. * Adding adgext_id5_id_link_type parameter to ID5 object. --- modules/adgenerationBidAdapter.js | 25 ++++++++- .../modules/adgenerationBidAdapter_spec.js | 56 +++++++++++++++++-- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index 94527c5d051..b48d9d350e3 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -25,13 +25,16 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - const ADGENE_PREBID_VERSION = '1.3.0'; + const ADGENE_PREBID_VERSION = '1.4.0'; let serverRequests = []; for (let i = 0, len = validBidRequests.length; i < len; i++) { const validReq = validBidRequests[i]; const DEBUG_URL = 'https://api-test.scaleout.jp/adsv/v1'; const URL = 'https://d.socdm.com/adsv/v1'; const url = validReq.params.debug ? DEBUG_URL : URL; + const criteoId = getCriteoId(validReq); + const id5id = getId5Id(validReq); + const id5LinkType = getId5LinkType(validReq); let data = ``; data = tryAppendQueryString(data, 'posall', 'SSPLOC'); const id = getBidIdParameter('id', validReq.params); @@ -45,10 +48,14 @@ export const spec = { data = tryAppendQueryString(data, 'pbver', '$prebid.version$'); data = tryAppendQueryString(data, 'sdkname', 'prebidjs'); data = tryAppendQueryString(data, 'adapterver', ADGENE_PREBID_VERSION); + data = tryAppendQueryString(data, 'adgext_criteo_id', criteoId); + data = tryAppendQueryString(data, 'adgext_id5_id', id5id); + data = tryAppendQueryString(data, 'adgext_id5_id_link_type', id5LinkType); // native以外にvideo等の対応が入った場合は要修正 if (!validReq.mediaTypes || !validReq.mediaTypes.native) { data = tryAppendQueryString(data, 'imark', '1'); } + // TODO: is 'page' the right value here? data = tryAppendQueryString(data, 'tp', bidderRequest.refererInfo.page); if (isIos()) { @@ -275,6 +282,22 @@ function getCurrencyType() { * @param validReq request * @return {null|string} */ +function getCriteoId(validReq) { + return (validReq.userId && validReq.userId.criteoId) ? validReq.userId.criteoId : null +} + +function getId5Id(validReq) { + return validId5(validReq) ? validReq.userId.id5id.uid : null +} + +function getId5LinkType(validReq) { + return validId5(validReq) ? validReq.userId.id5id.ext.linkType : null +} + +function validId5(validReq) { + return validReq.userId && validReq.userId.id5id && validReq.userId.id5id.uid && validReq.userId.id5id.ext.linkType +} + function getHyperId(validReq) { if (validReq.userId && validReq.userId.novatiq && validReq.userId.novatiq.snowflake.syncResponse === 1) { return validReq.userId.novatiq.snowflake.id; diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js index ac5ab220880..8583c226e50 100644 --- a/test/spec/modules/adgenerationBidAdapter_spec.js +++ b/test/spec/modules/adgenerationBidAdapter_spec.js @@ -101,6 +101,41 @@ describe('AdgenerationAdapter', function () { snowflake: {'id': 'novatiqId', syncResponse: 1} } } + }, + { // bannerWithAdgextCriteoId + bidder: 'adg', + params: { + id: '58278', // banner + }, + adUnitCode: 'adunit-code', + sizes: [[320, 100]], + bidId: '2f6ac468a9c15e', + bidderRequestId: '14a9f773e30243', + auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', + transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a', + userId: { + criteoId: 'criteo-id-test-1234567890' + } + }, + { // bannerWithAdgextId5Id + bidder: 'adg', + params: { + id: '58278', // banner + }, + adUnitCode: 'adunit-code', + sizes: [[320, 100]], + bidId: '2f6ac468a9c15e', + bidderRequestId: '14a9f773e30243', + auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', + transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a', + userId: { + id5id: { + ext: { + linkType: 2 + }, + uid: 'id5-id-test-1234567890' + } + } } ]; const bidderRequest = { @@ -109,10 +144,12 @@ describe('AdgenerationAdapter', function () { } }; const data = { - banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.3.0&imark=1&tp=https%3A%2F%2Fexample.com`, - bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.3.0&imark=1&tp=https%3A%2F%2Fexample.com`, - native: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.3.0&tp=https%3A%2F%2Fexample.com`, - bannerWithHyperId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.3.0&imark=1&tp=https%3A%2F%2Fexample.com&hyper_id=novatiqId`, + banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.4.0&imark=1&tp=https%3A%2F%2Fexample.com`, + bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.4.0&imark=1&tp=https%3A%2F%2Fexample.com`, + native: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.4.0&tp=https%3A%2F%2Fexample.com`, + bannerWithHyperId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.4.0&imark=1&tp=https%3A%2F%2Fexample.com&hyper_id=novatiqId`, + bannerWithAdgextCriteoId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.4.0&adgext_criteo_id=criteo-id-test-1234567890&imark=1&tp=https%3A%2F%2Fexample.com`, + bannerWithAdgextId5Id: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.4.0&adgext_id5_id=id5-id-test-1234567890&adgext_id5_id_link_type=2&imark=1&tp=https%3A%2F%2Fexample.com`, }; it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests, bidderRequest)[0]; @@ -149,6 +186,17 @@ describe('AdgenerationAdapter', function () { }); expect(request.data).to.equal(data.bannerWithHyperId); }); + + it('should attache params to the bannerWithAdgextCriteoId request', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[3]; + expect(request.data).to.equal(data.bannerWithAdgextCriteoId); + }); + + it('should attache params to the bannerWithAdgextId5Id request', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[4]; + expect(request.data).to.equal(data.bannerWithAdgextId5Id); + }); + it('allows setConfig to set bidder currency for JPY', function () { config.setConfig({ currency: { From b09f9f060ef6cfb55b168c6030e9710c31ece727 Mon Sep 17 00:00:00 2001 From: ako-adot <90328748+ako-adot@users.noreply.github.com> Date: Mon, 11 Jul 2022 20:26:33 +0200 Subject: [PATCH 009/569] ADOT Bid Adapter: handle schain & pubProvidedId (#8670) * [DSP-3689] feature: add pubProvidedId to user (#6) [DSP-3689] feature: add pubProvidedId to user * [DSP-3690] feature: Handle schain object (#7) --- modules/adotBidAdapter.js | 11 ++++++++++- test/spec/modules/adotBidAdapter_spec.js | 21 ++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 869cd69a830..af9cb8b5008 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -62,6 +62,9 @@ function getOpenRTBSiteObject(bidderRequest) { name: domain, publisher: { id: publisherId + }, + ext: { + schain: bidderRequest.schain } }; } @@ -83,7 +86,13 @@ function getOpenRTBDeviceObject() { */ function getOpenRTBUserObject(bidderRequest) { if (!bidderRequest || !bidderRequest.gdprConsent || !isStr(bidderRequest.gdprConsent.consentString)) return null; - return { ext: { consent: bidderRequest.gdprConsent.consentString } }; + + return { + ext: { + consent: bidderRequest.gdprConsent.consentString, + pubProvidedId: bidderRequest.userId && bidderRequest.userId.pubProvidedId, + }, + }; } /** diff --git a/test/spec/modules/adotBidAdapter_spec.js b/test/spec/modules/adotBidAdapter_spec.js index 8851df37d34..34252e00f9e 100644 --- a/test/spec/modules/adotBidAdapter_spec.js +++ b/test/spec/modules/adotBidAdapter_spec.js @@ -28,7 +28,7 @@ describe('Adot Adapter', function () { it('should build request (banner)', function () { const bidderRequestId = 'bidderRequestId'; const validBidRequests = [{ bidderRequestId, mediaTypes: {} }, { bidderRequestId, bidId: 'bidId', mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { placementId: 'placementId', adUnitCode: 200 } }]; - const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true } }; + const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, schain: { ver: '1.0' } }; const request = spec.buildRequests(validBidRequests, bidderRequest); const buildBidRequestResponse = { @@ -54,10 +54,11 @@ describe('Adot Adapter', function () { publisher: { // id: 'adot' id: undefined - } + }, + ext: { schain: { ver: '1.0' } } }, device: { ua: navigator.userAgent, language: navigator.language }, - user: { ext: { consent: bidderRequest.gdprConsent.consentString } }, + user: { ext: { consent: bidderRequest.gdprConsent.consentString, pubProvidedId: 'userId' } }, regs: { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies } }, ext: { adot: { adapter_version: 'v2.0.0' }, @@ -76,7 +77,7 @@ describe('Adot Adapter', function () { it('should build request (native)', function () { const bidderRequestId = 'bidderRequestId'; const validBidRequests = [{ bidderRequestId, mediaTypes: {} }, { bidderRequestId, bidId: 'bidId', mediaTypes: { native: { title: { required: true, len: 50, sizes: [[300, 250]] }, wrong: {}, image: {} } }, params: { placementId: 'placementId', adUnitCode: 200 } }]; - const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true } }; + const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, schain: { ver: '1.0' } }; const request = spec.buildRequests(validBidRequests, bidderRequest); const buildBidRequestResponse = { @@ -101,10 +102,11 @@ describe('Adot Adapter', function () { publisher: { // id: 'adot' id: undefined - } + }, + ext: { schain: { ver: '1.0' } } }, device: { ua: navigator.userAgent, language: navigator.language }, - user: { ext: { consent: bidderRequest.gdprConsent.consentString } }, + user: { ext: { consent: bidderRequest.gdprConsent.consentString, pubProvidedId: 'userId' } }, regs: { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies } }, ext: { adot: { adapter_version: 'v2.0.0' }, @@ -123,7 +125,7 @@ describe('Adot Adapter', function () { it('should build request (video)', function () { const bidderRequestId = 'bidderRequestId'; const validBidRequests = [{ bidderRequestId, mediaTypes: {} }, { bidderRequestId, bidId: 'bidId', mediaTypes: { video: { playerSize: [[300, 250]], minduration: 1, maxduration: 2, api: 'api', linearity: 'linearity', mimes: [], placement: 'placement', playbackmethod: 'playbackmethod', protocols: 'protocol', startdelay: 'startdelay' } }, params: { placementId: 'placementId', adUnitCode: 200 } }]; - const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true } }; + const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, schain: { ver: '1.0' } }; const request = spec.buildRequests(validBidRequests, bidderRequest); const buildBidRequestResponse = { @@ -160,10 +162,11 @@ describe('Adot Adapter', function () { publisher: { // id: 'adot' id: undefined - } + }, + ext: { schain: { ver: '1.0' } } }, device: { ua: navigator.userAgent, language: navigator.language }, - user: { ext: { consent: bidderRequest.gdprConsent.consentString } }, + user: { ext: { consent: bidderRequest.gdprConsent.consentString, pubProvidedId: 'userId' } }, regs: { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies } }, ext: { adot: { adapter_version: 'v2.0.0' }, From 5166dc9e13a7e24c34da6615b342a2bec636c345 Mon Sep 17 00:00:00 2001 From: Prebid-bydata <71428180+Prebid-bydata@users.noreply.github.com> Date: Tue, 12 Jul 2022 04:25:24 +0530 Subject: [PATCH 010/569] byData Analytics Adapter : send data on bidwon, update adunit path, & add some test params (#8621) * data on bidwon-adunit path-some test paramets added * update method for query string parameter * removed unpadded space * using StorageManager set/get value on local storage Co-authored-by: Jitendra-quizlet --- modules/byDataAnalyticsAdapter.js | 246 ++++++++++++------ modules/byDataAnalyticsAdapter.md | 11 +- .../modules/byDataAnalyticsAdapter_spec.js | 98 +++++-- 3 files changed, 251 insertions(+), 104 deletions(-) diff --git a/modules/byDataAnalyticsAdapter.js b/modules/byDataAnalyticsAdapter.js index ef6e1a503ee..1bf47cd467f 100644 --- a/modules/byDataAnalyticsAdapter.js +++ b/modules/byDataAnalyticsAdapter.js @@ -5,38 +5,80 @@ import enc from 'crypto-js/enc-utf8'; import adapter from '../src/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { auctionManager } from '../src/auctionManager.js'; import { ajax } from '../src/ajax.js'; -const secretKey = 'bydata@123456'; -const { EVENTS: { NO_BID, BID_TIMEOUT, AUCTION_END } } = CONSTANTS; -const DEFAULT_EVENT_URL = 'https://pbjs-stream.bydata.com/topics/prebid'; -const analyticsType = 'endpoint'; -var payload = {}; -var bdNbTo = { 'to': [], 'nb': [] }; -let initOptions = {}; +const versionCode = '4.4.1' +const secretKey = 'bydata@123456' +const { EVENTS: { NO_BID, BID_TIMEOUT, AUCTION_END, AUCTION_INIT, BID_WON } } = CONSTANTS +const DEFAULT_EVENT_URL = 'https://pbjs-stream.bydata.com/topics/prebid' +const analyticsType = 'endpoint' +const isBydata = isKeyInUrl('bydata_debug') +const adunitsMap = {} +const storage = getStorageManager(); +let initOptions = {} +var payload = {} +var winPayload = {} +var isDataSend = window.asc_data || false +var bdNbTo = { 'to': [], 'nb': [] } +/* method used for testing parameters */ +function isKeyInUrl(name) { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const param = urlParams.get(name) + return param +} + +/* return ad unit full path wrt custom ad unit code */ +function getAdunitName(code) { + var name = code; + for (const [key, value] of Object.entries(adunitsMap)) { + if (key === code) { name = value; } + } + return name; +} + +/* EVENT: auction init */ +function onAuctionStart(t) { + /* map of ad unit code - ad unit full path */ + t.adUnits && t.adUnits.length && t.adUnits.forEach((adu) => { + const { code, adunit } = adu + adunitsMap[code] = adunit + }); +} + +/* EVENT: bid timeout */ function onBidTimeout(t) { if (payload['visitor_data'] && t && t.length > 0) { - bdNbTo['to'] = t; + bdNbTo['to'] = t } } +/* EVENT: no bid */ function onNoBidData(t) { if (payload['visitor_data'] && t) { - bdNbTo['nb'].push(t); + bdNbTo['nb'].push(t) + } +} + +/* EVENT: bid won */ +function onBidWon(t) { + const { isCorrectOption } = initOptions + if (isCorrectOption && (isDataSend || isBydata)) { + ascAdapter.getBidWonData(t) + ascAdapter.sendPayload(winPayload) } } +/* EVENT: auction end */ function onAuctionEnd(t) { - _logInfo('onAuctionEnd', t); - const {isCorrectOption, logFrequency} = initOptions; - var value = Math.floor(Math.random() * 10000 + 1); - _logInfo(' value - frequency ', (value + '-' + logFrequency)); + const { isCorrectOption } = initOptions; setTimeout(() => { - if (isCorrectOption && value < logFrequency) { + if (isCorrectOption && (isDataSend || isBydata)) { ascAdapter.dataProcess(t); - addKeyForPrebidWinningAndWinningsBids(); - ascAdapter.sendPayload(); + ascAdapter.sendPayload(payload); } }, 500); } @@ -44,6 +86,9 @@ function onAuctionEnd(t) { const ascAdapter = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType: analyticsType }), { track({ eventType, args }) { switch (eventType) { + case AUCTION_INIT: + onAuctionStart(args); + break; case NO_BID: onNoBidData(args); break; @@ -53,6 +98,9 @@ const ascAdapter = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType case AUCTION_END: onAuctionEnd(args); break; + case BID_WON: + onBidWon(args); + break; default: break; } @@ -62,9 +110,8 @@ const ascAdapter = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType // save the base class function ascAdapter.originEnableAnalytics = ascAdapter.enableAnalytics; // override enableAnalytics so we can get access to the config passed in from the page -ascAdapter.enableAnalytics = function(config) { +ascAdapter.enableAnalytics = function (config) { if (this.initConfig(config)) { - _logInfo('initiated:', initOptions); initOptions.isCorrectOption && ascAdapter.getVisitorData(); ascAdapter.originEnableAnalytics(config); } @@ -73,7 +120,7 @@ ascAdapter.enableAnalytics = function(config) { ascAdapter.initConfig = function (config) { let isCorrectOption = true; initOptions = {}; - _logInfo('initConfig', config); + var rndNum = Math.floor(Math.random() * 10000 + 1); initOptions.options = deepClone(config.options); initOptions.clientId = initOptions.options.clientId || null; initOptions.logFrequency = initOptions.options.logFrequency; @@ -81,13 +128,41 @@ ascAdapter.initConfig = function (config) { _logError('"options.clientId" should not empty!!'); isCorrectOption = false; } + if (rndNum <= initOptions.logFrequency) { window.asc_data = isDataSend = true; } initOptions.isCorrectOption = isCorrectOption; this.initOptions = initOptions; return isCorrectOption; }; -ascAdapter.getVisitorData = function(data = {}) { - var ua = data.userId ? data : {}; +ascAdapter.getBidWonData = function(t) { + const { auctionId, adUnitCode, size, requestId, bidder, timeToRespond, currency, mediaType, cpm } = t + const aun = getAdunitName(adUnitCode) + winPayload['aid'] = auctionId + winPayload['as'] = ''; + winPayload['auctionData'] = []; + var data = {} + data['au'] = aun + data['auc'] = adUnitCode + data['aus'] = size + data['bid'] = requestId + data['bidadv'] = bidder + data['br_pb_mg'] = cpm + data['br_tr'] = timeToRespond + data['bradv'] = bidder + data['brid'] = requestId + data['brs'] = size + data['cur'] = currency + data['inb'] = 0 + data['ito'] = 0 + data['ipwb'] = 1 + data['iwb'] = 1 + data['mt'] = mediaType + winPayload['auctionData'].push(data) + return winPayload +} + +ascAdapter.getVisitorData = function (data = {}) { + var ua = data.uid ? data : {}; var module = { options: [], header: [window.navigator.platform, window.navigator.userAgent, window.navigator.appVersion, window.navigator.vendor, window.opera], @@ -152,7 +227,7 @@ ascAdapter.getVisitorData = function(data = {}) { crypto.getRandomValues(buffer); buffer[6] = (buffer[6] & ~176) | 64; buffer[8] = (buffer[8] & ~64) | 128; - var hex = Array.prototype.map.call(new Uint8Array(buffer), function(x) { + var hex = Array.prototype.map.call(new Uint8Array(buffer), function (x) { return ('00' + x.toString(16)).slice(-2); }).join(''); return hex.slice(0, 5) + '-' + hex.slice(5, 9) + '-' + hex.slice(9, 13) + '-' + hex.slice(13, 18); @@ -182,34 +257,46 @@ ascAdapter.getVisitorData = function(data = {}) { var signedToken = token + '.' + signature; return signedToken; } - const {clientId} = initOptions; - var userId = window.localStorage.getItem('userId'); + function detectWidth() { + return window.screen.width || (window.innerWidth && document.documentElement.clientWidth) ? Math.min(window.innerWidth, document.documentElement.clientWidth) : window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth; + } + function giveDeviceTypeOnScreenSize() { + var _dWidth = detectWidth(); + return _dWidth > 1024 ? 'Desktop' : (_dWidth <= 1024 && _dWidth >= 768) ? 'Tablet' : 'Mobile'; + } + + const { clientId } = initOptions; + var userId = storage.getDataFromLocalStorage('userId') if (!userId) { userId = generateUid(); - window.localStorage.setItem('userId', userId); + storage.setDataInLocalStorage('userId', userId); } - var screenSize = {width: window.screen.width, height: window.screen.height}; - var deviceType = window.navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i) ? 'Mobile' : 'Desktop'; + var screenSize = { width: window.screen.width, height: window.screen.height }; + var deviceType = giveDeviceTypeOnScreenSize(); var e = module.init(); - if (!ua['userId']) { - ua['userId'] = userId; - ua['client_id'] = clientId; - ua['plateform_name'] = e.os.name; - ua['os_version'] = e.os.version; - ua['browser_name'] = e.browser.name; - ua['browser_version'] = e.browser.version; - ua['screen_size'] = screenSize; - ua['device_type'] = deviceType; - ua['time_zone'] = window.Intl.DateTimeFormat().resolvedOptions().timeZone; + if (!ua['uid']) { + ua['uid'] = userId; + ua['cid'] = clientId; + ua['pid'] = window.location.hostname; + ua['os'] = e.os.name; + ua['osv'] = e.os.version; + ua['br'] = e.browser.name; + ua['brv'] = e.browser.version; + ua['ss'] = screenSize; + ua['de'] = deviceType; + ua['tz'] = window.Intl.DateTimeFormat().resolvedOptions().timeZone; } var signedToken = getJWToken(ua); payload['visitor_data'] = signedToken; + winPayload['visitor_data'] = signedToken; return signedToken; } -ascAdapter.dataProcess = function(t) { - payload['auction_id'] = t.auctionId; - payload['auction_start'] = t.timestamp; +ascAdapter.dataProcess = function (t) { + if (isBydata) { payload['bydata_debug'] = 'true'; } + _logInfo('fulldata - ', t); + payload['aid'] = t.auctionId; + payload['as'] = t.timestamp; payload['auctionData'] = []; var bidderRequestsData = []; var bidsReceivedData = []; t.bidderRequests && t.bidderRequests.forEach(bidReq => { @@ -228,69 +315,80 @@ ascAdapter.dataProcess = function(t) { bidderRequestsData.push(pObj); }); t.bidsReceived && t.bidsReceived.forEach(bid => { - const {requestId, bidder, width, height, cpm, currency, timeToRespond, adUnitCode} = bid; - bidsReceivedData.push({requestId, bidder, width, height, cpm, currency, timeToRespond, adUnitCode}); + const { requestId, bidder, width, height, cpm, currency, timeToRespond, adUnitCode } = bid; + bidsReceivedData.push({ requestId, bidder, width, height, cpm, currency, timeToRespond, adUnitCode }); }); bidderRequestsData.length > 0 && bidderRequestsData.forEach(bdObj => { var bdsArray = bdObj['bids']; bdsArray.forEach(bid => { - const {adUnitCode, sizes, bidder, bidId, mediaTypes} = bid; + const { adUnitCode, sizes, bidder, bidId, mediaTypes } = bid; sizes.forEach(size => { var sstr = size[0] + 'x' + size[1] - payload['auctionData'].push({adUnit: adUnitCode, size: sstr, media_type: mediaTypes[0], bids_bidder: bidder, bids_bid_id: bidId}); + payload['auctionData'].push({ au: getAdunitName(adUnitCode), auc: adUnitCode, aus: sstr, mt: mediaTypes[0], bidadv: bidder, bid: bidId, inb: 0, ito: 0, ipwb: 0, iwb: 0 }); }); }); }); + bidsReceivedData.length > 0 && bidsReceivedData.forEach(bdRecived => { - const {requestId, bidder, width, height, cpm, currency, timeToRespond} = bdRecived; + const { requestId, bidder, width, height, cpm, currency, timeToRespond } = bdRecived; payload['auctionData'].forEach(rwData => { - if (rwData['bids_bid_id'] === requestId && rwData['size'] === width + 'x' + height) { - rwData['br_request_id'] = requestId; rwData['br_bidder'] = bidder; rwData['br_pb_mg'] = cpm; - rwData['br_currency'] = currency; rwData['br_time_to_respond'] = timeToRespond; rwData['br_size'] = width + 'x' + height; + if (rwData['bid'] === requestId && rwData['aus'] === width + 'x' + height) { + rwData['brid'] = requestId; rwData['bradv'] = bidder; rwData['br_pb_mg'] = cpm; + rwData['cur'] = currency; rwData['br_tr'] = timeToRespond; rwData['brs'] = width + 'x' + height; } }) }); + + var prebidWinningBids = auctionManager.getBidsReceived().filter(bid => bid.status === CONSTANTS.BID_STATUS.BID_TARGETING_SET); + prebidWinningBids && prebidWinningBids.length > 0 && prebidWinningBids.forEach(pbbid => { + payload['auctionData'] && payload['auctionData'].forEach(rwData => { + if (rwData['bid'] === pbbid.requestId && rwData['brs'] === pbbid.size) { + rwData['ipwb'] = 1; + } + }); + }) + + var winningBids = auctionManager.getAllWinningBids(); + winningBids && winningBids.length > 0 && winningBids.forEach(wBid => { + payload['auctionData'] && payload['auctionData'].forEach(rwData => { + if (rwData['bid'] === wBid.requestId && rwData['brs'] === wBid.size) { + rwData['iwb'] = 1; + } + }); + }) + payload['auctionData'] && payload['auctionData'].length > 0 && payload['auctionData'].forEach(u => { bdNbTo['to'].forEach(i => { - if (u.bids_bid_id === i.bidId) u.is_timeout = 1; + if (u.bid === i.bidId) u.ito = 1; }); bdNbTo['nb'].forEach(i => { - if (u.adUnit === i.adUnitCode && u.bids_bidder === i.bidder && u.bids_bid_id === i.bidId) { u.is_nobid = 1; } + if (u.bidadv === i.bidder && u.bid === i.bidId) { u.inb = 1; } }) }); return payload; } -ascAdapter.sendPayload = function () { - var obj = { 'records': [ { 'value': payload } ] }; +ascAdapter.sendPayload = function (data) { + var obj = { 'records': [{ 'value': data }] }; let strJSON = JSON.stringify(obj); - _logInfo(' sendPayload ', JSON.stringify(obj)); - ajax(DEFAULT_EVENT_URL, undefined, strJSON, { + sendDataOnKf(strJSON); +} + +function sendDataOnKf(dataObj) { + ajax(DEFAULT_EVENT_URL, { + success: function () { + _logInfo('send data success'); + }, + error: function (e) { + _logInfo('send data error', e); + } + }, dataObj, { contentType: 'application/vnd.kafka.json.v2+json', method: 'POST', withCredentials: true }); } -function addKeyForPrebidWinningAndWinningsBids() { - var prebidWinningBids = $$PREBID_GLOBAL$$.getAllPrebidWinningBids(); - var winningBids = $$PREBID_GLOBAL$$.getAllWinningBids(); - prebidWinningBids && prebidWinningBids.length > 0 && prebidWinningBids.forEach(pbbid => { - payload['auctionData'] && payload['auctionData'].forEach(rwData => { - if (rwData['bids_bid_id'] === pbbid.requestId && rwData['br_size'] === pbbid.size) { - rwData['is_prebid_winning_bid'] = 1; - } - }); - }) - winningBids && winningBids.length > 0 && winningBids.forEach(wBid => { - payload['auctionData'] && payload['auctionData'].forEach(rwData => { - if (rwData['bids_bid_id'] === wBid.requestId && rwData['br_size'] === wBid.size) { - rwData['is_winning_bid'] = 1; - } - }); - }) -} - adapterManager.registerAnalyticsAdapter({ adapter: ascAdapter, code: 'bydata' @@ -305,7 +403,7 @@ function _logError(message) { } function buildLogMessage(message) { - return 'Bydata Prebid Analytics: ' + message; + return 'Bydata Prebid Analytics ' + versionCode + ':' + message; } export default ascAdapter; diff --git a/modules/byDataAnalyticsAdapter.md b/modules/byDataAnalyticsAdapter.md index 84207d8b3a1..a0780ecb514 100644 --- a/modules/byDataAnalyticsAdapter.md +++ b/modules/byDataAnalyticsAdapter.md @@ -1,7 +1,7 @@ # Overview layout: Analytics Adapter -title: Ascendeum Pvt Ltd. (https://ascendeum.com/) +title: byData. (https://bydata.com/) description: Bydata Analytics Adapter modulecode: byDataAnalyticsAdapter gdpr_supported: false (EU GDPR support) @@ -13,11 +13,12 @@ enable_download: false (in case you don't want users of the website to dow Module Name: Bydata Analytics Adapter Module Type: Analytics Adapter -Maintainer: Ascendeum +Maintainer: byData # Description -Analytics adapter for https://ascendeum.com/. Contact engineering@ascendeum.com for information. +Analytics adapter for https://bydata.com/. Contact admin@byData.com for information. + # Test Parameters @@ -25,10 +26,8 @@ Analytics adapter for https://ascendeum.com/. Contact engineering@ascendeum.com { provider: 'bydata', options : { - clientId: "ASCENDEUM_PROVIDED_CLIENT_ID", + clientId: "please contact byData team to get a clientId", logFrequency : 100, // Sample Rate Default - 1% } } ``` - - diff --git a/test/spec/modules/byDataAnalyticsAdapter_spec.js b/test/spec/modules/byDataAnalyticsAdapter_spec.js index 90b4e1d53a6..1346284695c 100644 --- a/test/spec/modules/byDataAnalyticsAdapter_spec.js +++ b/test/spec/modules/byDataAnalyticsAdapter_spec.js @@ -9,18 +9,19 @@ const initOptions = { logFrequency: 1, }; let userData = { - userId: '5da77-ec87-277b-8e7a5', - client_id: 'asc00000', - plateform_name: 'Macintosh', - os_version: 10.157, - browser_name: 'Chrome', - browser_version: 92.04515107, - screen_size: { - width: 1440, - height: 900 + 'uid': '271a8-2b86-f4a4-f59bc', + 'cid': 'asc00000', + 'pid': 'www.letsrun.com', + 'os': 'Macintosh', + 'osv': 10.157, + 'br': 'Chrome', + 'brv': 103, + 'ss': { + 'width': 1792, + 'height': 1120 }, - device_type: 'Desktop', - time_zone: 'Asia/Calcutta' + 'de': 'Desktop', + 'tz': 'Asia/Calcutta' }; let bidTimeoutArgs = [{ auctionId, @@ -39,6 +40,18 @@ let noBidArgs = { src: 'client', transactionId: 'c8ee3914-1ee0-4ce6-9126-748d5692188c' } +let bidWonArgs = { + auctionId, + adUnitCode: 'div-gpt-ad-mrec1', + size: '300x250', + requestId: '15c86b6c10d3746', + bidder: 'appnexus', + timeToRespond: 114, + currency: 'USD', + mediaType: 'display', + cpm: 0.50 +} + let auctionEndArgs = { adUnitCodes: ['div-gpt-ad-mrec1'], adUnits: [{ @@ -74,21 +87,54 @@ let auctionEndArgs = { }] } let expectedDataArgs = { - visitor_data: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1ZGE3Ny1lYzg3LTI3N2ItOGU3YTUiLCJjbGllbnRfaWQiOiJhc2MwMDAwMCIsInBsYXRlZm9ybV9uYW1lIjoiTWFjaW50b3NoIiwib3NfdmVyc2lvbiI6MTAuMTU3LCJicm93c2VyX25hbWUiOiJDaHJvbWUiLCJicm93c2VyX3ZlcnNpb24iOjkyLjA0NTE1MTA3LCJzY3JlZW5fc2l6ZSI6eyJ3aWR0aCI6MTQ0MCwiaGVpZ2h0Ijo5MDB9LCJkZXZpY2VfdHlwZSI6IkRlc2t0b3AiLCJ0aW1lX3pvbmUiOiJBc2lhL0NhbGN1dHRhIn0.jNKjsb3Q-ZjkVMcbss_dQFOmu_GdkGqd7t9MbRmqlG4YEMorcJHhUVmUuPi-9pKvC9_t4XlgjED90UieCvdxCQ', - auction_id: auctionId, - auction_start: 1627973484504, + visitor_data: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIyNzFhOC0yYjg2LWY0YTQtZjU5YmMiLCJjaWQiOiJhc2MwMDAwMCIsInBpZCI6Ind3dy5sZXRzcnVuLmNvbSIsIm9zIjoiTWFjaW50b3NoIiwib3N2IjoxMC4xNTcsImJyIjoiQ2hyb21lIiwiYnJ2IjoxMDMsInNzIjp7IndpZHRoIjoxNzkyLCJoZWlnaHQiOjExMjB9LCJkZSI6IkRlc2t0b3AiLCJ0eiI6IkFzaWEvQ2FsY3V0dGEifQ.Oj3qnh--t06XO-foVmrMJCGqFfOBed09A-f7LZX5rtfBf4w1_RNRZ4F3on4TMPLonSa7GgzbcEfJS9G_amnleQ', + aid: auctionId, + as: 1627973484504, auctionData: [ { - 'adUnit': 'div-gpt-ad-mrec1', - 'size': '300x250', - 'media_type': 'display', - 'bids_bidder': 'appnexus', - 'bids_bid_id': '14480e9832f2d2b' + au: 'div-gpt-ad-mrec1', + auc: 'div-gpt-ad-mrec1', + aus: '300x250', + bidadv: 'appnexus', + bid: '14480e9832f2d2b', + inb: 0, + ito: 0, + ipwb: 0, + iwb: 0, + mt: 'display', }, { - 'adUnit': 'div-gpt-ad-mrec1', - 'size': '250x250', - 'media_type': 'display', - 'bids_bidder': 'appnexus', - 'bids_bid_id': '14480e9832f2d2b' + au: 'div-gpt-ad-mrec1', + auc: 'div-gpt-ad-mrec1', + aus: '250x250', + bidadv: 'appnexus', + bid: '14480e9832f2d2b', + inb: 0, + ito: 0, + ipwb: 0, + iwb: 0, + mt: 'display', + }] +} +let expectedBidWonArgs = { + visitor_data: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIyNzFhOC0yYjg2LWY0YTQtZjU5YmMiLCJjaWQiOiJhc2MwMDAwMCIsInBpZCI6Ind3dy5sZXRzcnVuLmNvbSIsIm9zIjoiTWFjaW50b3NoIiwib3N2IjoxMC4xNTcsImJyIjoiQ2hyb21lIiwiYnJ2IjoxMDMsInNzIjp7IndpZHRoIjoxNzkyLCJoZWlnaHQiOjExMjB9LCJkZSI6IkRlc2t0b3AiLCJ0eiI6IkFzaWEvQ2FsY3V0dGEifQ.Oj3qnh--t06XO-foVmrMJCGqFfOBed09A-f7LZX5rtfBf4w1_RNRZ4F3on4TMPLonSa7GgzbcEfJS9G_amnleQ', + aid: auctionId, + as: '', + auctionData: [{ + au: 'div-gpt-ad-mrec1', + auc: 'div-gpt-ad-mrec1', + aus: '300x250', + bid: '15c86b6c10d3746', + bidadv: 'appnexus', + br_pb_mg: 0.50, + br_tr: 114, + bradv: 'appnexus', + brid: '15c86b6c10d3746', + brs: '300x250', + cur: 'USD', + inb: 0, + ito: 0, + ipwb: 1, + iwb: 1, + mt: 'display', }] } @@ -130,10 +176,14 @@ describe('byData Analytics Adapter ', () => { it('sends and formatted auction data ', function () { events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeoutArgs); events.emit(constants.EVENTS.NO_BID, noBidArgs); + events.emit(constants.EVENTS.BID_WON, bidWonArgs) var userToken = ascAdapter.getVisitorData(userData); var newAuData = ascAdapter.dataProcess(auctionEndArgs); + var newBwData = ascAdapter.getBidWonData(bidWonArgs); newAuData['visitor_data'] = userToken; + newBwData['visitor_data'] = userToken; expect(newAuData).to.deep.equal(expectedDataArgs); + expect(newBwData).to.deep.equal(expectedBidWonArgs); }); }); }); From 28d51c6fef9b4cbabb35751152525603e894f1e4 Mon Sep 17 00:00:00 2001 From: TM Date: Tue, 12 Jul 2022 13:04:53 +0200 Subject: [PATCH 011/569] Adrino Adapter: New config parameter for a custom adserver domain (#8667) * New config parameter for a custom adserver domain * test fix * new test Co-authored-by: Tomasz Mielcarz --- modules/adrinoBidAdapter.js | 12 ++++++++++-- modules/adrinoBidAdapter.md | 6 ++++++ test/spec/modules/adrinoBidAdapter_spec.js | 22 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/modules/adrinoBidAdapter.js b/modules/adrinoBidAdapter.js index ed898c46cac..e7cac348b3e 100644 --- a/modules/adrinoBidAdapter.js +++ b/modules/adrinoBidAdapter.js @@ -1,6 +1,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {triggerPixel} from '../src/utils.js'; import {NATIVE} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; const BIDDER_CODE = 'adrino'; const REQUEST_METHOD = 'POST'; @@ -12,6 +13,10 @@ export const spec = { gvlid: GVLID, supportedMediaTypes: [NATIVE], + getBidderConfig: function (property) { + return config.getConfig(`${BIDDER_CODE}.${property}`); + }, + isBidRequestValid: function (bid) { return !!(bid.bidId) && !!(bid.params) && @@ -26,6 +31,8 @@ export const spec = { const bidRequests = []; for (let i = 0; i < validBidRequests.length; i++) { + let host = this.getBidderConfig('host') || BIDDER_HOST; + let requestData = { bidId: validBidRequests[i].bidId, nativeParams: validBidRequests[i].nativeParams, @@ -44,7 +51,7 @@ export const spec = { bidRequests.push({ method: REQUEST_METHOD, - url: BIDDER_HOST + '/bidder/bid/', + url: host + '/bidder/bid/', data: requestData, options: { contentType: 'application/json', @@ -67,7 +74,8 @@ export const spec = { onBidWon: function (bid) { if (bid['requestId']) { - triggerPixel(BIDDER_HOST + '/bidder/won/' + bid['requestId']); + let host = this.getBidderConfig('host') || BIDDER_HOST; + triggerPixel(host + '/bidder/won/' + bid['requestId']); } } }; diff --git a/modules/adrinoBidAdapter.md b/modules/adrinoBidAdapter.md index 5ec63a72736..ab655f700fc 100644 --- a/modules/adrinoBidAdapter.md +++ b/modules/adrinoBidAdapter.md @@ -13,6 +13,12 @@ Module connects to Adrino bidder to fetch bids. Only native format is supported. # Test Parameters ``` +pbjs.setConfig({ + adrino: { + host: 'https://custom-domain.adrino.io' + } +}); + var adUnits = [ code: '/12345678/prebid_native_example_1', mediaTypes: { diff --git a/test/spec/modules/adrinoBidAdapter_spec.js b/test/spec/modules/adrinoBidAdapter_spec.js index a7b7007dcf8..a321eb60677 100644 --- a/test/spec/modules/adrinoBidAdapter_spec.js +++ b/test/spec/modules/adrinoBidAdapter_spec.js @@ -1,8 +1,13 @@ import { expect } from 'chai'; import { spec } from 'modules/adrinoBidAdapter.js'; +import {config} from '../../../src/config.js'; import * as utils from '../../../src/utils'; describe('adrinoBidAdapter', function () { + afterEach(() => { + config.resetConfig(); + }); + describe('isBidRequestValid', function () { const validBid = { bidder: 'adrino', @@ -72,6 +77,23 @@ describe('adrinoBidAdapter', function () { auctionId: '01234567891234', }; + it('should build the request correctly with custom domain', function () { + config.setConfig({adrino: { host: 'https://stg-prebid-bidder.adrino.io' }}); + const result = spec.buildRequests( + [ bidRequest ], + { refererInfo: { page: 'http://example.com/' } } + ); + expect(result.length).to.equal(1); + expect(result[0].method).to.equal('POST'); + expect(result[0].url).to.equal('https://stg-prebid-bidder.adrino.io/bidder/bid/'); + expect(result[0].data.bidId).to.equal('12345678901234'); + expect(result[0].data.placementHash).to.equal('abcdef123456'); + expect(result[0].data.referer).to.equal('http://example.com/'); + expect(result[0].data.userAgent).to.equal(navigator.userAgent); + expect(result[0].data).to.have.property('nativeParams'); + expect(result[0].data).not.to.have.property('gdprConsent'); + }); + it('should build the request correctly with gdpr', function () { const result = spec.buildRequests( [ bidRequest ], From 099a4df48acd82a268e4ef54ce6684c11c640cbd Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Tue, 12 Jul 2022 13:48:54 +0200 Subject: [PATCH 012/569] Adnuntius Bid Adapter: Video Instream capabilities (#8631) * Added option to pass a user id through ortb2. * RTD provider added. * check if use cookie is present in config, and use it accordingly. * Adding test for no cookie url addition. * Adnuntius RTD Provider tests added * Adnuntius RTD Module * Removed adnuntius RTD provider from this pull request. * Fix error * Added video instream capabilities --- modules/adnuntiusBidAdapter.js | 34 ++++- package-lock.json | 2 +- test/spec/modules/adnuntiusBidAdapter_spec.js | 140 ++++++++++++++++-- 3 files changed, 153 insertions(+), 23 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index fed69c48d18..b9499af7d9d 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -1,12 +1,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { isStr, deepAccess } from '../src/utils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { isStr, deepAccess, logInfo } from '../src/utils.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adnuntius'; const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i'; const GVLID = 855; +const DEFAULT_VAST_VERSION = 'vast4' const checkSegment = function (segment) { if (isStr(segment)) return segment; @@ -27,7 +28,7 @@ const getSegmentsFromOrtb = function (ortb2) { } const handleMeta = function () { - const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}) + const storage = getStorageManager({ gvlid: GVLID, bidderCode: BIDDER_CODE }) let adnMeta = null if (storage.localStorageIsEnabled()) { adnMeta = JSON.parse(storage.getDataFromLocalStorage('adn.metaData')) @@ -45,7 +46,7 @@ const getUsi = function (meta, ortb2, bidderRequest) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { return !!(bid.bidId || (bid.params.member && bid.params.invCode)); }, @@ -67,15 +68,20 @@ export const spec = { request.push('tzo=' + tzo) request.push('format=json') + if (gdprApplies !== undefined) request.push('consentString=' + consentString); if (segments.length > 0) request.push('segments=' + segments.join(',')); if (usi) request.push('userId=' + usi); if (bidderConfig.useCookie === false) request.push('noCookies=true') for (var i = 0; i < validBidRequests.length; i++) { const bid = validBidRequests[i] - const network = bid.params.network || 'network'; + let network = bid.params.network || 'network'; const targeting = bid.params.targeting || {}; + if (bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.context !== 'outstream') { + network += '_video' + } + bidRequests[network] = bidRequests[network] || []; bidRequests[network].push(bid); @@ -89,9 +95,11 @@ export const spec = { const networkKeys = Object.keys(networks) for (var j = 0; j < networkKeys.length; j++) { const network = networkKeys[j]; + const networkRequest = [...request] + if (network.indexOf('_video') > -1) { networkRequest.push('tt=' + DEFAULT_VAST_VERSION) } requests.push({ method: 'POST', - url: ENDPOINT_URL + '?' + request.join('&'), + url: ENDPOINT_URL + '?' + networkRequest.join('&'), data: JSON.stringify(networks[network]), bid: bidRequests[network] }); @@ -106,7 +114,7 @@ export const spec = { if (adUnit.matchedAdCount >= 1) { const ad = adUnit.ads[0]; const effectiveCpm = (ad.bid) ? ad.bid.amount * 1000 : 0; - return { + const adResponse = { ...response, [adUnit.targetId]: { requestId: adUnit.targetId, @@ -122,9 +130,19 @@ export const spec = { }, netRevenue: false, ttl: 360, - ad: adUnit.html } } + + if (adUnit.vastXml) { + adResponse[adUnit.targetId].vastXml = adUnit.vastXml + adResponse[adUnit.targetId].mediaType = 'video' + } else { + adResponse[adUnit.targetId].ad = adUnit.html + } + + logInfo('BID', adResponse) + + return adResponse } else return response }, {}); diff --git a/package-lock.json b/package-lock.json index 622fff0792f..dfb3c6d93ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "6.28.0-pre", + "version": "7.4.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 817229cd381..307830dd4ef 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -11,7 +11,7 @@ describe('adnuntiusBidAdapter', function () { const GVLID = 855; const usi = utils.generateUUID() const meta = [{ key: 'usi', value: usi }] - const storage = getStorageManager({gvlid: GVLID, moduleName: 'adnuntius'}) + const storage = getStorageManager({ gvlid: GVLID, moduleName: 'adnuntius' }) storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) beforeEach(function () { @@ -29,12 +29,13 @@ describe('adnuntiusBidAdapter', function () { const tzo = new Date().getTimezoneOffset(); const ENDPOINT_URL = `${URL}${tzo}&format=json&userId=${usi}`; + const ENDPOINT_URL_VIDEO = `${URL}${tzo}&format=json&userId=${usi}&tt=vast4`; const ENDPOINT_URL_NOCOOKIE = `${URL}${tzo}&format=json&userId=${usi}&noCookies=true`; const ENDPOINT_URL_SEGMENTS = `${URL}${tzo}&format=json&segments=segment1,segment2,segment3&userId=${usi}`; const ENDPOINT_URL_CONSENT = `${URL}${tzo}&format=json&consentString=consentString&userId=${usi}`; const adapter = newBidder(spec); - const bidRequests = [ + const bidderRequests = [ { bidId: '123', bidder: 'adnuntius', @@ -46,6 +47,23 @@ describe('adnuntiusBidAdapter', function () { } ] + const videoBidderRequest = [ + { + bidId: '123', + bidder: 'adnuntius', + params: { + auId: '8b6bc', + network: 'adnuntius', + }, + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + } + ] + const singleBidRequest = { bid: [ { @@ -54,6 +72,10 @@ describe('adnuntiusBidAdapter', function () { ] } + const videoBidRequest = { + bid: videoBidderRequest + } + const serverResponse = { body: { 'adUnits': [ @@ -117,6 +139,69 @@ describe('adnuntiusBidAdapter', function () { ] } } + const serverVideoResponse = { + body: { + 'adUnits': [ + { + 'auId': '000000000008b6bc', + 'targetId': '123', + 'html': '

hi!

', + 'matchedAdCount': 1, + 'responseId': 'adn-rsp-1460129238', + 'ads': [ + { + 'destinationUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com', + 'assets': { + 'image': { + 'cdnId': 'https://assets.adnuntius.com/oEmZa5uYjxENfA1R692FVn6qIveFpO8wUbpyF2xSOCc.jpg', + 'width': '980', + 'height': '120' + } + }, + 'clickUrl': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'urls': { + 'destination': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN?ct=2501&r=http%3A%2F%2Fgoogle.com' + }, + 'urlsEsc': { + 'destination': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com' + }, + 'destinationUrls': { + 'destination': 'http://google.com' + }, + 'cpm': { 'amount': 5.0, 'currency': 'NOK' }, + 'bid': { 'amount': 0.005, 'currency': 'NOK' }, + 'cost': { 'amount': 0.005, 'currency': 'NOK' }, + 'impressionTrackingUrls': [], + 'impressionTrackingUrlsEsc': [], + 'adId': 'adn-id-1347343135', + 'selectedColumn': '0', + 'selectedColumnPosition': '0', + 'renderedPixel': 'https://delivery.adnuntius.com/b/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', + 'renderedPixelEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fb%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', + 'visibleUrl': 'https://delivery.adnuntius.com/s?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'visibleUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fs%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'viewUrl': 'https://delivery.adnuntius.com/v?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'viewUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fv%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'rt': '52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'creativeWidth': '980', + 'creativeHeight': '120', + 'creativeId': 'wgkq587vgtpchsx1', + 'lineItemId': 'scyjdyv3mzgdsnpf', + 'layoutId': 'sw6gtws2rdj1kwby', + 'layoutName': 'Responsive image' + }, + + ] + }, + { + 'auId': '000000000008b6bc', + 'targetId': '456', + 'matchedAdCount': 0, + 'responseId': 'adn-rsp-1460129238', + } + ] + } + } describe('inherited functions', function () { it('exists and is a function', function () { @@ -126,13 +211,13 @@ describe('adnuntiusBidAdapter', function () { describe('isBidRequestValid', function () { it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); + expect(spec.isBidRequestValid(bidderRequests[0])).to.equal(true); }); }); describe('buildRequests', function () { it('Test requests', function () { - const request = spec.buildRequests(bidRequests, {}); + const request = spec.buildRequests(bidderRequests, {}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('bid'); const bid = request[0].bid[0] @@ -143,6 +228,16 @@ describe('adnuntiusBidAdapter', function () { expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\"}],\"metaData\":{\"usi\":\"' + usi + '\"}}'); }); + it('Test Video requests', function () { + const request = spec.buildRequests(videoBidderRequest, {}); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('bid'); + const bid = request[0].bid[0] + expect(bid).to.have.property('bidId'); + expect(request[0]).to.have.property('url'); + expect(request[0].url).to.equal(ENDPOINT_URL_VIDEO); + }); + it('should pass segments if available in config', function () { const ortb2 = { user: { @@ -157,7 +252,7 @@ describe('adnuntiusBidAdapter', function () { } }; - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests, {ortb2})); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); @@ -179,7 +274,7 @@ describe('adnuntiusBidAdapter', function () { } }; - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests, {ortb2})); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); @@ -188,14 +283,14 @@ describe('adnuntiusBidAdapter', function () { describe('user privacy', function () { it('should send GDPR Consent data if gdprApplies', function () { - let request = spec.buildRequests(bidRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); + let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_CONSENT); }); it('should not send GDPR Consent data if gdprApplies equals undefined', function () { - let request = spec.buildRequests(bidRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); + let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL); @@ -215,7 +310,7 @@ describe('adnuntiusBidAdapter', function () { } } - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests, {ortb2})); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); @@ -237,7 +332,7 @@ describe('adnuntiusBidAdapter', function () { } }; - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests, {ortb2})); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); @@ -250,7 +345,7 @@ describe('adnuntiusBidAdapter', function () { } }; - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests, {ortb2})); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL); @@ -259,14 +354,14 @@ describe('adnuntiusBidAdapter', function () { describe('user privacy', function () { it('should send GDPR Consent data if gdprApplies', function () { - let request = spec.buildRequests(bidRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); + let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_CONSENT); }); it('should not send GDPR Consent data if gdprApplies equals undefined', function () { - let request = spec.buildRequests(bidRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); + let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL); @@ -282,7 +377,7 @@ describe('adnuntiusBidAdapter', function () { } }); - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests, {})); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {})); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_NOCOOKIE); @@ -307,4 +402,21 @@ describe('adnuntiusBidAdapter', function () { expect(interpretedResponse[0].ttl).to.equal(360); }); }); + describe('interpretVideoResponse', function () { + it('should return valid response when passed valid server response', function () { + const interpretedResponse = spec.interpretResponse(serverVideoResponse, videoBidRequest); + const ad = serverVideoResponse.body.adUnits[0].ads[0] + expect(interpretedResponse).to.have.lengthOf(1); + expect(interpretedResponse[0].cpm).to.equal(ad.cpm.amount); + expect(interpretedResponse[0].width).to.equal(Number(ad.creativeWidth)); + expect(interpretedResponse[0].height).to.equal(Number(ad.creativeHeight)); + expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); + expect(interpretedResponse[0].currency).to.equal(ad.bid.currency); + expect(interpretedResponse[0].netRevenue).to.equal(false); + expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[0].meta.advertiserDomains).to.have.lengthOf(1); + expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('google.com'); + expect(interpretedResponse[0].vastXml).to.equal(serverVideoResponse.body.adUnits[0].vastXml); + }); + }); }); From 5681e50c43cf3a38e4bc1f5d391d26cc776beba6 Mon Sep 17 00:00:00 2001 From: Jason Piros Date: Tue, 12 Jul 2022 04:52:23 -0700 Subject: [PATCH 013/569] consumableBidAdapter - add shared ID (#8605) --- modules/consumableBidAdapter.js | 13 +++- .../spec/modules/consumableBidAdapter_spec.js | 75 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index afdf34b72c1..5c34fbe8c64 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -1,4 +1,4 @@ -import { logWarn, createTrackPixelHtml } from '../src/utils.js'; +import { logWarn, createTrackPixelHtml, deepAccess, isArray, deepSetValue } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'consumable'; @@ -78,6 +78,8 @@ export const spec = { } }); + handleEids(data, validBidRequests); + ret.data = JSON.stringify(data); ret.bidRequest = validBidRequests; ret.bidderRequest = bidderRequest; @@ -234,4 +236,13 @@ function retrieveAd(decision, unitId, unitName) { return ad; } +function handleEids(data, validBidRequests) { + let bidUserIdAsEids = deepAccess(validBidRequests, '0.userIdAsEids'); + if (isArray(bidUserIdAsEids) && bidUserIdAsEids.length > 0) { + deepSetValue(data, 'user.eids', bidUserIdAsEids); + } else { + deepSetValue(data, 'user.eids', undefined); + } +} + registerBidder(spec); diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index b70cd6fe631..23851c0da40 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -1,6 +1,9 @@ import {expect} from 'chai'; import {spec} from 'modules/consumableBidAdapter.js'; import {createBid} from 'src/bidfactory.js'; +import {config} from 'src/config.js'; +import {deepClone} from 'src/utils.js'; +import { createEidsArray } from 'modules/userId/eids.js'; const ENDPOINT = 'https://e.serverbid.com/api/v2'; const SMARTSYNC_CALLBACK = 'serverbidCallBids'; @@ -425,4 +428,76 @@ describe('Consumable BidAdapter', function () { expect(opts.length).to.equal(1); }); }); + describe('unifiedId from userId module', function() { + let sandbox, bidderRequest; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + bidderRequest = deepClone(BIDDER_REQUEST_1); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('Request should have unifiedId config params', function() { + bidderRequest.bidRequest[0].userId = {}; + bidderRequest.bidRequest[0].userId.tdid = 'TTD_ID'; + bidderRequest.bidRequest[0].userIdAsEids = createEidsArray(bidderRequest.bidRequest[0].userId); + let request = spec.buildRequests(bidderRequest.bidRequest, BIDDER_REQUEST_1); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': 'TTD_ID', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should have adsrvrOrgId from UserId Module if config and userId module both have TTD ID', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': 'TTD_ID_FROM_CONFIG', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2022-06-21T09:47:00' + } + }; + return config[key]; + }); + bidderRequest.bidRequest[0].userId = {}; + bidderRequest.bidRequest[0].userId.tdid = 'TTD_ID'; + bidderRequest.bidRequest[0].userIdAsEids = createEidsArray(bidderRequest.bidRequest[0].userId); + let request = spec.buildRequests(bidderRequest.bidRequest, BIDDER_REQUEST_1); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': 'TTD_ID', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should NOT have adsrvrOrgId params if userId is NOT object', function() { + let request = spec.buildRequests(bidderRequest.bidRequest, BIDDER_REQUEST_1); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + + it('Request should NOT have adsrvrOrgId params if userId.tdid is NOT string', function() { + bidderRequest.bidRequest[0].userId = { + tdid: 1234 + }; + let request = spec.buildRequests(bidderRequest.bidRequest, BIDDER_REQUEST_1); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + }); }); From f2ca64f98c4b2b1e6a136fc3d892155cc2aabab7 Mon Sep 17 00:00:00 2001 From: OronW <41260031+OronW@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:05:12 +0300 Subject: [PATCH 014/569] add support for loop number. Add test coverage for loop number (#8673) --- modules/minutemediaBidAdapter.js | 1 + test/spec/modules/minutemediaBidAdapter_spec.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/modules/minutemediaBidAdapter.js b/modules/minutemediaBidAdapter.js index beb6618631d..d953558bf31 100644 --- a/modules/minutemediaBidAdapter.js +++ b/modules/minutemediaBidAdapter.js @@ -286,6 +286,7 @@ function generateBidParameters(bid, bidderRequest) { sizes: sizesArray, floorPrice: Math.max(getFloor(bid, mediaType), params.floorPrice), bidId: getBidIdParameter('bidId', bid), + loop: getBidIdParameter('bidderRequestsCount', bid), bidderRequestId: getBidIdParameter('bidderRequestId', bid), transactionId: getBidIdParameter('transactionId', bid), }; diff --git a/test/spec/modules/minutemediaBidAdapter_spec.js b/test/spec/modules/minutemediaBidAdapter_spec.js index cce08e615a3..bbd23918031 100644 --- a/test/spec/modules/minutemediaBidAdapter_spec.js +++ b/test/spec/modules/minutemediaBidAdapter_spec.js @@ -53,6 +53,7 @@ describe('minutemediaAdapter', function () { 'org': 'jdye8weeyirk00000001' }, 'bidId': '299ffc8cca0b87', + 'loop': 1, 'bidderRequestId': '1144f487e563f9', 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', 'mediaTypes': { @@ -71,6 +72,7 @@ describe('minutemediaAdapter', function () { 'org': 'jdye8weeyirk00000001' }, 'bidId': '299ffc8cca0b87', + 'loop': 1, 'bidderRequestId': '1144f487e563f9', 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', 'mediaTypes': { @@ -91,6 +93,7 @@ describe('minutemediaAdapter', function () { 'testMode': true }, 'bidId': '299ffc8cca0b87', + 'loop': 2, 'bidderRequestId': '1144f487e563f9', 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', } From ea29fa16cbea64e85cbbc4c6dab85cea2afb6e0c Mon Sep 17 00:00:00 2001 From: Yohan Boutin Date: Tue, 12 Jul 2022 15:15:14 +0200 Subject: [PATCH 015/569] Seedtag Bid Adapter: add compliance to usp/ccpa (#8602) * add compliance to usp/ccpa * add unit test * typo in the unit test name * more un it tests --- modules/seedtagBidAdapter.js | 4 ++++ test/spec/modules/seedtagBidAdapter_spec.js | 25 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index cc76363fc9a..8c5ab41bd6e 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -236,6 +236,10 @@ export const spec = { payload['cd'] = bidderRequest.gdprConsent.consentString; } + if (bidderRequest.uspConsent) { + payload['uspConsent'] = bidderRequest.uspConsent + } + const payloadString = JSON.stringify(payload) return { method: 'POST', diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index 958c3066631..092fd3ebf9a 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -292,6 +292,31 @@ describe('Seedtag Adapter', function () { it('should expose gvlid', function () { expect(spec.gvlid).to.equal(157); }); + it('should handle uspConsent', function () { + const uspConsent = '1---'; + + bidderRequest['uspConsent'] = uspConsent; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.uspConsent).to.exist; + expect(payload.uspConsent).to.equal(uspConsent); + }); + + it("shouldn't send uspConsent when not available", function () { + const uspConsent = undefined; + + bidderRequest['uspConsent'] = uspConsent; + + const request = spec.buildRequests( + validBidRequests, + bidderRequest + ); + const payload = JSON.parse(request.data); + + expect(payload.uspConsent).to.not.exist; + }); }); }); From cdbc08ab19e1985b4cbd36a77639c70cab9e53f9 Mon Sep 17 00:00:00 2001 From: fndigrazia Date: Tue, 12 Jul 2022 11:20:44 -0300 Subject: [PATCH 016/569] Eplanning Bid Adapter: viewability refactor and fix in search the divs (#8610) * eplanning viewability refactor and fix in search the divs * change observer configuration name * unused variable is removed * kick off circleci tests manually Co-authored-by: Chris Huie --- modules/eplanningBidAdapter.js | 179 ++++++++---------- test/spec/modules/eplanningBidAdapter_spec.js | 76 ++++++-- 2 files changed, 140 insertions(+), 115 deletions(-) diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index ca2cbfd9908..a6adab1e9b9 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -1,4 +1,4 @@ -import {getWindowSelf, isEmpty, parseSizesInput} from '../src/utils.js'; +import {getWindowSelf, isEmpty, parseSizesInput, isGptPubadsDefined, isSlotMatchingAdUnitCode} from '../src/utils.js'; import {getGlobal} from '../src/prebidGlobal.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getStorageManager} from '../src/storageManager.js'; @@ -291,13 +291,25 @@ function getCharset() { function waitForElementsPresent(elements) { const observer = new MutationObserver(function (mutationList, observer) { + let index; + let adView; if (mutationList && Array.isArray(mutationList)) { mutationList.forEach(mr => { if (mr && mr.addedNodes && Array.isArray(mr.addedNodes)) { mr.addedNodes.forEach(ad => { - let index = elements.indexOf(ad.id); + index = elements.indexOf(ad.id); + adView = ad; + if (index < 0) { + elements.forEach(code => { + let div = _getAdSlotHTMLElement(code); + if (div && div.contains(ad) && div.getBoundingClientRect().width > 0) { + index = elements.indexOf(div.id); + adView = div; + } + }); + } if (index >= 0) { - registerViewability(ad); + registerViewability(adView, elements[index]); elements.splice(index, 1); if (!elements.length) { observer.disconnect(); @@ -318,19 +330,41 @@ function waitForElementsPresent(elements) { }); } -function registerViewability(div) { +function registerViewability(div, name) { visibilityHandler({ - name: div.id, + name: name, div: div }); } +function _mapAdUnitPathToElementId(adUnitCode) { + if (isGptPubadsDefined()) { + // eslint-disable-next-line no-undef + const adSlots = googletag.pubads().getSlots(); + const isMatchingAdSlot = isSlotMatchingAdUnitCode(adUnitCode); + + for (let i = 0; i < adSlots.length; i++) { + if (isMatchingAdSlot(adSlots[i])) { + const id = adSlots[i].getSlotElementId(); + return id; + } + } + } + + return null; +} + +function _getAdSlotHTMLElement(adUnitCode) { + return document.getElementById(adUnitCode) || + document.getElementById(_mapAdUnitPathToElementId(adUnitCode)); +} + function registerViewabilityAllBids(bids) { let elementsNotPresent = []; bids.forEach(bid => { - let div = document.getElementById(bid.adUnitCode); + let div = _getAdSlotHTMLElement(bid.adUnitCode); if (div) { - registerViewability(div); + registerViewability(div, bid.adUnitCode); } else { elementsNotPresent.push(bid.adUnitCode); } @@ -345,114 +379,65 @@ function getViewabilityTracker() { let VIEWABILITY_TIME = 1000; let VIEWABILITY_MIN_RATIO = 0.5; let publicApi; - let context; - - function segmentIsOutsideTheVisibleRange(visibleRangeEnd, p1, p2) { - return p1 > visibleRangeEnd || p2 < 0; - } - - function segmentBeginsBeforeTheVisibleRange(p1) { - return p1 < 0; - } - - function segmentEndsAfterTheVisibleRange(visibleRangeEnd, p2) { - return p2 < visibleRangeEnd; - } - - function axialVisibilityRatio(visibleRangeEnd, p1, p2) { - let visibilityRatio = 0; - if (!segmentIsOutsideTheVisibleRange(visibleRangeEnd, p1, p2)) { - if (segmentBeginsBeforeTheVisibleRange(p1)) { - visibilityRatio = p2 / (p2 - p1); + let observer; + let visibilityAds = {}; + + function intersectionCallback(entries) { + entries.forEach(function(entry) { + var adBox = entry.target; + if (entry.isIntersecting) { + if (entry.intersectionRatio >= VIEWABILITY_MIN_RATIO && entry.boundingClientRect && entry.boundingClientRect.height > 0 && entry.boundingClientRect.width > 0) { + visibilityAds[adBox.id] = true; + } } else { - visibilityRatio = segmentEndsAfterTheVisibleRange(visibleRangeEnd, p2) ? 1 : (visibleRangeEnd - p1) / (p2 - p1); + visibilityAds[adBox.id] = false; } - } - return visibilityRatio; - } - - function isNotHiddenByNonFriendlyIframe() { - try { return (window === window.top) || window.frameElement; } catch (e) {} - } - - function defineContext(e) { - try { - context = e && window.document.body.contains(e) ? window : (window.top.document.body.contains(e) ? top : undefined); - } catch (err) {} - return context; - } - - function getContext(e) { - return context; - } - - function verticalVisibilityRatio(position) { - return axialVisibilityRatio(getContext().innerHeight, position.top, position.bottom); - } - - function horizontalVisibilityRatio(position) { - return axialVisibilityRatio(getContext().innerWidth, position.left, position.right); - } - - function itIsNotHiddenByBannerAreaPosition(e) { - let position = e.getBoundingClientRect(); - return (verticalVisibilityRatio(position) * horizontalVisibilityRatio(position)) > VIEWABILITY_MIN_RATIO; - } - - function itIsNotHiddenByDisplayStyleCascade(e) { - return e.offsetHeight > 0 && e.offsetWidth > 0; - } - - function itIsNotHiddenByOpacityStyleCascade(e) { - let s = e.style; - let p = e.parentNode; - return !(s && parseFloat(s.opacity) === 0) && (!p || itIsNotHiddenByOpacityStyleCascade(p)); - } - - function itIsNotHiddenByVisibilityStyleCascade(e) { - return getContext().getComputedStyle(e).visibility !== 'hidden'; - } - - function itIsNotHiddenByTabFocus() { - try { return getContext().top.document.hasFocus(); } catch (e) {} - } - - function isDefined(e) { - return (e !== null) && (typeof e !== 'undefined'); + }); } - function itIsNotHiddenByOrphanBranch() { - return isDefined(getContext()); + function observedElementIsVisible(element) { + return visibilityAds[element.id] && document.visibilityState && document.visibilityState === 'visible'; } - function isContextInAnIframe() { - return isDefined(getContext().frameElement); + function defineObserver() { + if (!observer) { + var observerConfig = { + root: null, + rootMargin: '0px', + threshold: [VIEWABILITY_MIN_RATIO] + }; + observer = new IntersectionObserver(intersectionCallback.bind(this), observerConfig); + } } - function processIntervalVisibilityStatus(elapsedVisibleIntervals, element, callback) { - let visibleIntervals = isVisible(element) ? (elapsedVisibleIntervals + 1) : 0; + let visibleIntervals = observedElementIsVisible(element) ? (elapsedVisibleIntervals + 1) : 0; if (visibleIntervals === TIME_PARTITIONS) { + stopObserveViewability(element) callback(); } else { setTimeout(processIntervalVisibilityStatus.bind(this, visibleIntervals, element, callback), VIEWABILITY_TIME / TIME_PARTITIONS); } } - function isVisible(element) { - defineContext(element); - return isNotHiddenByNonFriendlyIframe() && - itIsNotHiddenByOrphanBranch() && - itIsNotHiddenByTabFocus() && - itIsNotHiddenByDisplayStyleCascade(element) && - itIsNotHiddenByVisibilityStyleCascade(element) && - itIsNotHiddenByOpacityStyleCascade(element) && - itIsNotHiddenByBannerAreaPosition(element) && - (!isContextInAnIframe() || isVisible(getContext().frameElement)); + function stopObserveViewability(element) { + delete visibilityAds[element.id]; + observer.unobserve(element); + } + + function observeAds(element) { + observer.observe(element); + } + + function initAndVerifyVisibility(element, callback) { + if (element) { + defineObserver(); + observeAds(element); + processIntervalVisibilityStatus(0, element, callback); + } } publicApi = { - isVisible: isVisible, - onView: processIntervalVisibilityStatus.bind(this, 0) + onView: initAndVerifyVisibility.bind(this) }; return publicApi; diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index 52d2fb76115..db70f7956d7 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -6,6 +6,7 @@ import {init, getIds} from 'modules/userId/index.js'; import * as utils from 'src/utils.js'; import {hook} from '../../../src/hook.js'; import {getGlobal} from '../../../src/prebidGlobal.js'; +import { makeSlot } from '../integration/faker/googletag.js'; describe('E-Planning Adapter', function () { const adapter = newBidder('spec'); @@ -649,7 +650,24 @@ describe('E-Planning Adapter', function () { let element; let getBoundingClientRectStub; let sandbox = sinon.sandbox.create(); - let focusStub; + let intersectionObserverStub; + let intersectionCallback; + + function setIntersectionObserverMock(params) { + let fakeIntersectionObserver = (stateChange, options) => { + intersectionCallback = stateChange; + return { + unobserve: (element) => { + return element; + }, + observe: (element) => { + intersectionCallback([{'target': {'id': element.id}, 'isIntersecting': params[element.id].isIntersecting, 'intersectionRatio': params[element.id].ratio, 'boundingClientRect': {'width': params[element.id].width, 'height': params[element.id].height}}]); + }, + }; + }; + + intersectionObserverStub = sandbox.stub(window, 'IntersectionObserver').callsFake(fakeIntersectionObserver); + } function createElement(id) { element = document.createElement('div'); element.id = id || ADUNIT_CODE_VIEW; @@ -741,9 +759,6 @@ describe('E-Planning Adapter', function () { hasLocalStorageStub.returns(true); clock = sandbox.useFakeTimers(); - - focusStub = sandbox.stub(window.top.document, 'hasFocus'); - focusStub.returns(true); }); afterEach(function () { $$PREBID_GLOBAL$$.bidderSettings = {}; @@ -786,6 +801,7 @@ describe('E-Planning Adapter', function () { let respuesta; beforeEach(function () { createElementVisible(); + setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200}}); }); it('when you have a render', function() { respuesta = spec.buildRequests(bidRequests, bidderRequest); @@ -823,6 +839,7 @@ describe('E-Planning Adapter', function () { let respuesta; beforeEach(function () { createElementOutOfView(); + setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200}}); }); it('when you have a render', function() { @@ -848,6 +865,7 @@ describe('E-Planning Adapter', function () { let respuesta; it('should register visibility with more than 50%', function() { createPartiallyVisibleElement(); + setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 0.6, 'isIntersecting': true, 'width': 200, 'height': 200}}); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); @@ -856,6 +874,7 @@ describe('E-Planning Adapter', function () { }); it('you should not register visibility with less than 50%', function() { createPartiallyInvisibleElement(); + setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 0.4, 'isIntersecting': true, 'width': 200, 'height': 200}}); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); @@ -863,12 +882,29 @@ describe('E-Planning Adapter', function () { expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); }); + context('when element id is not equal to adunitcode', function() { + let respuesta; + it('should register visibility with more than 50%', function() { + const code = ADUNIT_CODE_VIEW; + const divId = 'div-gpt-ad-123'; + createPartiallyVisibleElement(divId); + window.googletag.pubads().setSlots([makeSlot({ code, divId })]); + setIntersectionObserverMock({[divId]: {'ratio': 0.6, 'isIntersecting': true, 'width': 200, 'height': 200}}); + + respuesta = spec.buildRequests(bidRequests, bidderRequest); + clock.tick(1005); + + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal('1'); + }); + }); context('when width or height of the element is zero', function() { beforeEach(function () { createElementVisible(); }); it('if the width is zero but the height is within the range', function() { element.style.width = '0px'; + setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 0.4, 'isIntersecting': true, 'width': 200, 'height': 200}}); spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); @@ -877,6 +913,7 @@ describe('E-Planning Adapter', function () { }); it('if the height is zero but the width is within the range', function() { element.style.height = '0px'; + setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 500, 'height': 0}}); spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); @@ -886,6 +923,7 @@ describe('E-Planning Adapter', function () { it('if both are zero', function() { element.style.height = '0px'; element.style.width = '0px'; + setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 0, 'height': 0}}); spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); @@ -893,16 +931,6 @@ describe('E-Planning Adapter', function () { expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); }); - context('when tab is inactive', function() { - it('I should not register if it is not in focus', function() { - createElementVisible(); - focusStub.returns(false); - spec.buildRequests(bidRequests, bidderRequest); - clock.tick(1005); - expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); - }); - }); context('segmentBeginsBeforeTheVisibleRange', function() { it('segmentBeginsBeforeTheVisibleRange', function() { createElementOutOfRange(); @@ -933,7 +961,11 @@ describe('E-Planning Adapter', function () { createElementVisible(ADUNIT_CODE_VIEW); createElementVisible(ADUNIT_CODE_VIEW2); createElementVisible(ADUNIT_CODE_VIEW3); - + setIntersectionObserverMock({ + [ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200}, + [ADUNIT_CODE_VIEW2]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200}, + [ADUNIT_CODE_VIEW3]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200} + }); respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { @@ -946,7 +978,11 @@ describe('E-Planning Adapter', function () { createElementOutOfView(ADUNIT_CODE_VIEW); createElementOutOfView(ADUNIT_CODE_VIEW2); createElementOutOfView(ADUNIT_CODE_VIEW3); - + setIntersectionObserverMock({ + [ADUNIT_CODE_VIEW]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200}, + [ADUNIT_CODE_VIEW2]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200}, + [ADUNIT_CODE_VIEW3]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200} + }); respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { @@ -960,7 +996,11 @@ describe('E-Planning Adapter', function () { createElementVisible(ADUNIT_CODE_VIEW); createElementOutOfView(ADUNIT_CODE_VIEW2); createElementOutOfView(ADUNIT_CODE_VIEW3); - + setIntersectionObserverMock({ + [ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200}, + [ADUNIT_CODE_VIEW2]: {'ratio': 0.3, 'isIntersecting': true, 'width': 200, 'height': 200}, + [ADUNIT_CODE_VIEW3]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200} + }); respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); expect(storage.getDataFromLocalStorage('pbsr_' + ADUNIT_CODE_VIEW)).to.equal('6'); @@ -989,7 +1029,7 @@ describe('E-Planning Adapter', function () { sandbox.restore(); }) - it('should add eids to the request', function() { + it('should add eids to the request ', function() { let bidRequests = [validBidView]; const expected_id5id = encodeURIComponent(JSON.stringify({ uid: 'ID5-ZHMOL_IfFSt7_lVYX8rBZc6GH3XMWyPQOBUfr4bm0g!', ext: { linkType: 1 } })); const request = spec.buildRequests(bidRequests, bidderRequest); From c09108bc14fa51be82936c44e755d6bf0ac85942 Mon Sep 17 00:00:00 2001 From: Phaneendra Hegde Date: Tue, 12 Jul 2022 20:06:08 +0530 Subject: [PATCH 017/569] PubxaiAnalyticsAdapter: Added extra fields to winning bid object (#8644) * Added extra fields to winning bid object * Update: Picking specific fields from floorDetail instead of including the entire object Co-authored-by: Phaneendra Hegde --- modules/pubxaiAnalyticsAdapter.js | 7 ++++++- test/spec/modules/pubxaiAnalyticsAdapter_spec.js | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index 669bd062206..3f35cd8ff79 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -91,7 +91,12 @@ function mapBidResponse(bidResponse, status) { } else { Object.assign(bid, { bidId: bidResponse.requestId, - floorProvider: events.floorDetail ? events.floorDetail.floorProvider : null, + floorProvider: events.floorDetail?.floorProvider || null, + floorFetchStatus: events.floorDetail?.fetchStatus || null, + floorLocation: events.floorDetail?.location || null, + floorModelVersion: events.floorDetail?.modelVersion || null, + floorSkipRate: events.floorDetail?.skipRate || 0, + isFloorSkipped: events.floorDetail?.skipped || false, isWinningBid: true, placementId: bidResponse.params ? deepAccess(bidResponse, 'params.0.placementId') : null, renderedSize: bidResponse.size, diff --git a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js index 63364b867be..1ad23b9a41e 100644 --- a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js @@ -624,6 +624,11 @@ describe('pubxai analytics adapter', function() { } }, 'floorProvider': 'PubXFloorProvider', + 'floorFetchStatus': 'success', + 'floorLocation': 'fetch', + 'floorModelVersion': 'test model 1.0', + 'floorSkipRate': 0, + 'isFloorSkipped': false, 'isWinningBid': true, 'mediaType': 'banner', 'netRevenue': true, From a2462cc1f968af4f7ddd3e28dfdc32160109f4a5 Mon Sep 17 00:00:00 2001 From: newspassid-prebid <107485317+newspassid-prebid@users.noreply.github.com> Date: Tue, 12 Jul 2022 20:14:05 +0100 Subject: [PATCH 018/569] Newspassid Bid Adapter: initial release (#8567) * New bid adapter: newspassid spec test spec test file for new bid adapter with biddercode: newspassid * New Bid Adapter: newspassid newspassid prebid JS bid adapter * fixed tests fixed tests * Rename newspassBidAdapter.js to newspassidBidAdapter.js renamed to correct bidder code * Update newspassidBidAdapter_spec.js * Deleted duplicate spec file moved bidAdapter.js file to correct folder (modules) and deleted duplicate spec file * fixes * Update newspassidBidAdapter.js * Create newspassid.md adding md file to modules folder as requested. * updated dev doc to modules filder fixes error in test params * Update newspassidBidAdapter.js fixing latest feedback from prebid.org review * Update newspassidBidAdapter.js fixing latest feedback * Update newspassidBidAdapter.js * updates per feedback * Update newspassidBidAdapter.js updating version number - re-committing to see if circle CI tests now pass * fixing location of files should fix circleCi failures! --- modules/newspassidBidAdapter.js | 649 ++++++ .../spec/modules/newspassidBidAdapter_spec.js | 1793 +++++++++++++++++ 2 files changed, 2442 insertions(+) create mode 100644 modules/newspassidBidAdapter.js create mode 100644 test/spec/modules/newspassidBidAdapter_spec.js diff --git a/modules/newspassidBidAdapter.js b/modules/newspassidBidAdapter.js new file mode 100644 index 00000000000..ee6ece2b033 --- /dev/null +++ b/modules/newspassidBidAdapter.js @@ -0,0 +1,649 @@ +import { logInfo, logError, deepAccess, logWarn, deepSetValue, isArray, contains, parseUrl } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; +import {getPriceBucketString} from '../src/cpmBucketManager.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +const BIDDER_CODE = 'newspassid'; +const ORIGIN = 'https://bidder.newspassid.com' // applies only to auction & cookie +const AUCTIONURI = '/openrtb2/auction'; +const NEWSPASSCOOKIESYNC = '/static/load-cookie.html'; +const NEWSPASSVERSION = '1.0.1'; +export const spec = { + version: NEWSPASSVERSION, + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + cookieSyncBag: {publisherId: null, siteId: null, userIdObject: {}}, // variables we want to make available to cookie sync + propertyBag: {config: null, pageId: null, buildRequestsStart: 0, buildRequestsEnd: 0, endpointOverride: null}, /* allow us to store vars in instance scope - needs to be an object to be mutable */ + config_defaults: { + 'logId': 'NEWSPASSID', + 'bidder': 'newspassid', + 'auctionUrl': ORIGIN + AUCTIONURI, + 'cookieSyncUrl': ORIGIN + NEWSPASSCOOKIESYNC + }, + loadConfiguredData(bid) { + if (this.propertyBag.config) { return; } + this.propertyBag.config = JSON.parse(JSON.stringify(this.config_defaults)); + let bidder = bid.bidder || 'newspassid'; + this.propertyBag.config.logId = bidder.toUpperCase(); + this.propertyBag.config.bidder = bidder; + let bidderConfig = config.getConfig(bidder) || {}; + logInfo('got bidderConfig: ', JSON.parse(JSON.stringify(bidderConfig))); + let arrGetParams = this.getGetParametersAsObject(); + if (bidderConfig.endpointOverride) { + if (bidderConfig.endpointOverride.origin) { + this.propertyBag.endpointOverride = bidderConfig.endpointOverride.origin; + this.propertyBag.config.auctionUrl = bidderConfig.endpointOverride.origin + AUCTIONURI; + this.propertyBag.config.cookieSyncUrl = bidderConfig.endpointOverride.origin + NEWSPASSCOOKIESYNC; + } + if (bidderConfig.endpointOverride.cookieSyncUrl) { + this.propertyBag.config.cookieSyncUrl = bidderConfig.endpointOverride.cookieSyncUrl; + } + if (bidderConfig.endpointOverride.auctionUrl) { + this.propertyBag.endpointOverride = bidderConfig.endpointOverride.auctionUrl; + this.propertyBag.config.auctionUrl = bidderConfig.endpointOverride.auctionUrl; + } + } + try { + if (arrGetParams.hasOwnProperty('auction')) { + logInfo('GET: setting auction endpoint to: ' + arrGetParams.auction); + this.propertyBag.config.auctionUrl = arrGetParams.auction; + } + if (arrGetParams.hasOwnProperty('cookiesync')) { + logInfo('GET: setting cookiesync to: ' + arrGetParams.cookiesync); + this.propertyBag.config.cookieSyncUrl = arrGetParams.cookiesync; + } + } catch (e) {} + logInfo('set propertyBag.config to', this.propertyBag.config); + }, + getAuctionUrl() { + return this.propertyBag.config.auctionUrl; + }, + getCookieSyncUrl() { + return this.propertyBag.config.cookieSyncUrl; + }, + isBidRequestValid(bid) { + this.loadConfiguredData(bid); + logInfo('isBidRequestValid : ', config.getConfig(), bid); + let adUnitCode = bid.adUnitCode; // adunit[n].code + let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED' + if (!(bid.params.hasOwnProperty('placementId'))) { + logError(err1.replace('{param}', 'placementId'), adUnitCode); + return false; + } + if (!this.isValidPlacementId(bid.params.placementId)) { + logError('VALIDATION FAILED : placementId must be exactly 10 numeric characters', adUnitCode); + return false; + } + if (!(bid.params.hasOwnProperty('publisherId'))) { + logError(err1.replace('{param}', 'publisherId'), adUnitCode); + return false; + } + if (!(bid.params.publisherId).toString().match(/^[a-zA-Z0-9\-]{12}$/)) { + logError('VALIDATION FAILED : publisherId must be exactly 12 alphanumeric characters including hyphens', adUnitCode); + return false; + } + if (!(bid.params.hasOwnProperty('siteId'))) { + logError(err1.replace('{param}', 'siteId'), adUnitCode); + return false; + } + if (!(bid.params.siteId).toString().match(/^[0-9]{10}$/)) { + logError('VALIDATION FAILED : siteId must be exactly 10 numeric characters', adUnitCode); + return false; + } + if (bid.params.hasOwnProperty('customParams')) { + logError('VALIDATION FAILED : customParams should be renamed to customData', adUnitCode); + return false; + } + if (bid.params.hasOwnProperty('customData')) { + if (!Array.isArray(bid.params.customData)) { + logError('VALIDATION FAILED : customData is not an Array', adUnitCode); + return false; + } + if (bid.params.customData.length < 1) { + logError('VALIDATION FAILED : customData is an array but does not contain any elements', adUnitCode); + return false; + } + if (!(bid.params.customData[0]).hasOwnProperty('targeting')) { + logError('VALIDATION FAILED : customData[0] does not contain "targeting"', adUnitCode); + return false; + } + if (typeof bid.params.customData[0]['targeting'] != 'object') { + logError('VALIDATION FAILED : customData[0] targeting is not an object', adUnitCode); + return false; + } + } + return true; + }, + isValidPlacementId(placementId) { + return placementId.toString().match(/^[0-9]{10}$/); + }, + buildRequests(validBidRequests, bidderRequest) { + this.loadConfiguredData(validBidRequests[0]); + this.propertyBag.buildRequestsStart = new Date().getTime(); + logInfo(`buildRequests time: ${this.propertyBag.buildRequestsStart} v ${NEWSPASSVERSION} validBidRequests`, JSON.parse(JSON.stringify(validBidRequests)), 'bidderRequest', JSON.parse(JSON.stringify(bidderRequest))); + if (this.blockTheRequest()) { + return []; + } + let htmlParams = {'publisherId': '', 'siteId': ''}; + if (validBidRequests.length > 0) { + this.cookieSyncBag.userIdObject = Object.assign(this.cookieSyncBag.userIdObject, this.findAllUserIds(validBidRequests[0])); + this.cookieSyncBag.siteId = deepAccess(validBidRequests[0], 'params.siteId'); + this.cookieSyncBag.publisherId = deepAccess(validBidRequests[0], 'params.publisherId'); + htmlParams = validBidRequests[0].params; + } + logInfo('cookie sync bag', this.cookieSyncBag); + let singleRequest = config.getConfig('newspassid.singleRequest'); + singleRequest = singleRequest !== false; // undefined & true will be true + logInfo(`config newspassid.singleRequest : `, singleRequest); + let npRequest = {}; // we only want to set specific properties on this, not validBidRequests[0].params + logInfo('going to get ortb2 from bidder request...'); + let fpd = deepAccess(bidderRequest, 'ortb2', null); + logInfo('got fpd: ', fpd); + if (fpd && deepAccess(fpd, 'user')) { + logInfo('added FPD user object'); + npRequest.user = fpd.user; + } + const getParams = this.getGetParametersAsObject(); + const isTestMode = getParams['nptestmode'] || null; // this can be any string, it's used for testing ads + npRequest.device = {'w': window.innerWidth, 'h': window.innerHeight}; + let placementIdOverrideFromGetParam = this.getPlacementIdOverrideFromGetParam(); // null or string + let schain = null; + let tosendtags = validBidRequests.map(npBidRequest => { + var obj = {}; + let placementId = placementIdOverrideFromGetParam || this.getPlacementId(npBidRequest); // prefer to use a valid override param, else the bidRequest placement Id + obj.id = npBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder newspass made bid for unknown request ID: mb7953.859498327448. Ignoring." + obj.tagid = placementId; + let parsed = parseUrl(getRefererInfo().page); + obj.secure = parsed.protocol === 'https' ? 1 : 0; + let arrBannerSizes = []; + if (!npBidRequest.hasOwnProperty('mediaTypes')) { + if (npBidRequest.hasOwnProperty('sizes')) { + logInfo('no mediaTypes detected - will use the sizes array in the config root'); + arrBannerSizes = npBidRequest.sizes; + } else { + logInfo('Cannot set sizes for banner type'); + } + } else { + if (npBidRequest.mediaTypes.hasOwnProperty(BANNER)) { + arrBannerSizes = npBidRequest.mediaTypes[BANNER].sizes; /* Note - if there is a sizes element in the config root it will be pushed into here */ + logInfo('setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes); + } + if (npBidRequest.mediaTypes.hasOwnProperty(NATIVE)) { + obj.native = npBidRequest.mediaTypes[NATIVE]; + logInfo('setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native); + } + } + if (arrBannerSizes.length > 0) { + obj.banner = { + topframe: 1, + w: arrBannerSizes[0][0] || 0, + h: arrBannerSizes[0][1] || 0, + format: arrBannerSizes.map(s => { + return {w: s[0], h: s[1]}; + }) + }; + } + obj.placementId = placementId; + deepSetValue(obj, 'ext.prebid', {'storedrequest': {'id': placementId}}); + obj.ext['newspassid'] = {}; + obj.ext['newspassid'].adUnitCode = npBidRequest.adUnitCode; // eg. 'mpu' + obj.ext['newspassid'].transactionId = npBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit + if (npBidRequest.params.hasOwnProperty('customData')) { + obj.ext['newspassid'].customData = npBidRequest.params.customData; + } + logInfo(`obj.ext.newspassid is `, obj.ext['newspassid']); + if (isTestMode != null) { + logInfo('setting isTestMode to ', isTestMode); + if (obj.ext['newspassid'].hasOwnProperty('customData')) { + for (let i = 0; i < obj.ext['newspassid'].customData.length; i++) { + obj.ext['newspassid'].customData[i]['targeting']['nptestmode'] = isTestMode; + } + } else { + obj.ext['newspassid'].customData = [{'settings': {}, 'targeting': {}}]; + obj.ext['newspassid'].customData[0].targeting['nptestmode'] = isTestMode; + } + } + if (fpd && deepAccess(fpd, 'site')) { + logInfo('adding fpd.site'); + if (deepAccess(obj, 'ext.newspassid.customData.0.targeting', false)) { + obj.ext.newspassid.customData[0].targeting = Object.assign(obj.ext.newspassid.customData[0].targeting, fpd.site); + } else { + deepSetValue(obj, 'ext.newspassid.customData.0.targeting', fpd.site); + } + } + if (!schain && deepAccess(npBidRequest, 'schain')) { + schain = npBidRequest.schain; + } + return obj; + }); + let extObj = {}; + extObj['newspassid'] = {}; + extObj['newspassid']['np_pb_v'] = NEWSPASSVERSION; + extObj['newspassid']['np_rw'] = placementIdOverrideFromGetParam ? 1 : 0; + if (validBidRequests.length > 0) { + let userIds = this.cookieSyncBag.userIdObject; // 2021-01-06 - slight optimisation - we've already found this info + if (userIds.hasOwnProperty('pubcid')) { + extObj['newspassid'].pubcid = userIds.pubcid; + } + } + extObj['newspassid'].pv = this.getPageId(); // attach the page ID that will be common to all auction calls for this page if refresh() is called + let whitelistAdserverKeys = config.getConfig('newspassid.np_whitelist_adserver_keys'); + let useWhitelistAdserverKeys = isArray(whitelistAdserverKeys) && whitelistAdserverKeys.length > 0; + extObj['newspassid']['np_kvp_rw'] = useWhitelistAdserverKeys ? 1 : 0; + if (getParams.hasOwnProperty('npf')) { extObj['newspassid']['npf'] = getParams.npf === 'true' || getParams.npf === '1' ? 1 : 0; } + if (getParams.hasOwnProperty('nppf')) { extObj['newspassid']['nppf'] = getParams.nppf === 'true' || getParams.nppf === '1' ? 1 : 0; } + if (getParams.hasOwnProperty('nprp') && getParams.nprp.match(/^[0-3]$/)) { extObj['newspassid']['nprp'] = parseInt(getParams.nprp); } + if (getParams.hasOwnProperty('npip') && getParams.npip.match(/^\d+$/)) { extObj['newspassid']['npip'] = parseInt(getParams.npip); } + if (this.propertyBag.endpointOverride != null) { extObj['newspassid']['origin'] = this.propertyBag.endpointOverride; } + let userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); // generate the UserIDs in the correct format for UserId module + npRequest.site = { + 'publisher': {'id': htmlParams.publisherId}, + 'page': getRefererInfo().page, + 'id': htmlParams.siteId + }; + npRequest.test = config.getConfig('debug') ? 1 : 0; + if (bidderRequest && bidderRequest.uspConsent) { + logInfo('ADDING USP consent info'); + deepSetValue(npRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } else { + logInfo('WILL NOT ADD USP consent info; no bidderRequest.uspConsent.'); + } + if (schain) { // we set this while iterating over the bids + logInfo('schain found'); + deepSetValue(npRequest, 'source.ext.schain', schain); + } + if (config.getConfig('coppa') === true) { + deepSetValue(npRequest, 'regs.coppa', 1); + } + if (singleRequest) { + logInfo('buildRequests starting to generate response for a single request'); + npRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. + npRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here? + npRequest.imp = tosendtags; + npRequest.ext = extObj; + deepSetValue(npRequest, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). + deepSetValue(npRequest, 'user.ext.eids', userExtEids); + var ret = { + method: 'POST', + url: this.getAuctionUrl(), + data: JSON.stringify(npRequest), + bidderRequest: bidderRequest + }; + logInfo('buildRequests request data for single = ', JSON.parse(JSON.stringify(npRequest))); + this.propertyBag.buildRequestsEnd = new Date().getTime(); + logInfo(`buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, ret); + return ret; + } + let arrRet = tosendtags.map(imp => { + logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); + let npRequestSingle = Object.assign({}, npRequest); + imp.ext['newspassid'].pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object + npRequestSingle.id = imp.ext['newspassid'].transactionId; // Unique ID of the bid request, provided by the exchange. + npRequestSingle.auctionId = imp.ext['newspassid'].transactionId; // not sure if this should be here? + npRequestSingle.imp = [imp]; + npRequestSingle.ext = extObj; + deepSetValue(npRequestSingle, 'source.tid', imp.ext['newspassid'].transactionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). + deepSetValue(npRequestSingle, 'user.ext.eids', userExtEids); + logInfo('buildRequests RequestSingle (for non-single) = ', npRequestSingle); + return { + method: 'POST', + url: this.getAuctionUrl(), + data: JSON.stringify(npRequestSingle), + bidderRequest: bidderRequest + }; + }); + this.propertyBag.buildRequestsEnd = new Date().getTime(); + logInfo(`buildRequests going to return for non-single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, arrRet); + return arrRet; + }, + interpretResponse(serverResponse, request) { + if (request && request.bidderRequest && request.bidderRequest.bids) { this.loadConfiguredData(request.bidderRequest.bids[0]); } + let startTime = new Date().getTime(); + logInfo(`interpretResponse time: ${startTime}. buildRequests done -> interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); + logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); + serverResponse = serverResponse.body || {}; + if (!serverResponse.hasOwnProperty('seatbid')) { + return []; + } + if (typeof serverResponse.seatbid !== 'object') { + return []; + } + let arrAllBids = []; + let enhancedAdserverTargeting = config.getConfig('newspassid.enhancedAdserverTargeting'); + logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); + if (typeof enhancedAdserverTargeting == 'undefined') { + enhancedAdserverTargeting = true; + } + logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); + serverResponse.seatbid = injectAdIdsIntoAllBidResponses(serverResponse.seatbid); // we now make sure that each bid in the bidresponse has a unique (within page) adId attribute. + serverResponse.seatbid = this.removeSingleBidderMultipleBids(serverResponse.seatbid); + let whitelistAdserverKeys = config.getConfig('newspassid.np_whitelist_adserver_keys'); + let useWhitelistAdserverKeys = isArray(whitelistAdserverKeys) && whitelistAdserverKeys.length > 0; + for (let i = 0; i < serverResponse.seatbid.length; i++) { + let sb = serverResponse.seatbid[i]; + for (let j = 0; j < sb.bid.length; j++) { + let thisRequestBid = this.getBidRequestForBidId(sb.bid[j].impid, request.bidderRequest.bids); + logInfo(`seatbid:${i}, bid:${j} Going to set default w h for seatbid/bidRequest`, sb.bid[j], thisRequestBid); + const {defaultWidth, defaultHeight} = defaultSize(thisRequestBid); + let thisBid = this.addStandardProperties(sb.bid[j], defaultWidth, defaultHeight); + thisBid.meta = {advertiserDomains: thisBid.adomain || []}; + let bidType = deepAccess(thisBid, 'ext.prebid.type'); + logInfo(`this bid type is : ${bidType}`, j); + let adserverTargeting = {}; + if (enhancedAdserverTargeting) { + let allBidsForThisBidid = this.getAllBidsForBidId(thisBid.bidId, serverResponse.seatbid); + logInfo('Going to iterate allBidsForThisBidId', allBidsForThisBidid); + Object.keys(allBidsForThisBidid).forEach((bidderName, index, ar2) => { + logInfo(`adding adserverTargeting for ${bidderName} for bidId ${thisBid.bidId}`); + adserverTargeting['np_' + bidderName] = bidderName; + adserverTargeting['np_' + bidderName + '_crid'] = String(allBidsForThisBidid[bidderName].crid); + adserverTargeting['np_' + bidderName + '_adv'] = String(allBidsForThisBidid[bidderName].adomain); + adserverTargeting['np_' + bidderName + '_adId'] = String(allBidsForThisBidid[bidderName].adId); + adserverTargeting['np_' + bidderName + '_pb_r'] = getRoundedBid(allBidsForThisBidid[bidderName].price, allBidsForThisBidid[bidderName].ext.prebid.type); + if (allBidsForThisBidid[bidderName].hasOwnProperty('dealid')) { + adserverTargeting['np_' + bidderName + '_dealid'] = String(allBidsForThisBidid[bidderName].dealid); + } + }); + } else { + logInfo(`newspassid.enhancedAdserverTargeting is set to false, no per-bid keys will be sent to adserver.`); + } + let {seat: winningSeat, bid: winningBid} = this.getWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); + adserverTargeting['np_auc_id'] = String(request.bidderRequest.auctionId); + adserverTargeting['np_winner'] = String(winningSeat); + adserverTargeting['np_bid'] = 'true'; + if (enhancedAdserverTargeting) { + adserverTargeting['np_imp_id'] = String(winningBid.impid); + adserverTargeting['np_pb_r'] = getRoundedBid(winningBid.price, bidType); + adserverTargeting['np_adId'] = String(winningBid.adId); + adserverTargeting['np_size'] = `${winningBid.width}x${winningBid.height}`; + } + if (useWhitelistAdserverKeys) { // delete any un-whitelisted keys + logInfo('Going to filter out adserver targeting keys not in the whitelist: ', whitelistAdserverKeys); + Object.keys(adserverTargeting).forEach(function(key) { if (whitelistAdserverKeys.indexOf(key) === -1) { delete adserverTargeting[key]; } }); + } + thisBid.adserverTargeting = adserverTargeting; + arrAllBids.push(thisBid); + } + } + let endTime = new Date().getTime(); + logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`, arrAllBids); + return arrAllBids; + }, + removeSingleBidderMultipleBids(seatbid) { + var ret = []; + for (let i = 0; i < seatbid.length; i++) { + let sb = seatbid[i]; + var retSeatbid = {'seat': sb.seat, 'bid': []}; + var bidIds = []; + for (let j = 0; j < sb.bid.length; j++) { + var candidate = sb.bid[j]; + if (contains(bidIds, candidate.impid)) { + continue; // we've already fully assessed this impid, found the highest bid from this seat for it + } + bidIds.push(candidate.impid); + for (let k = j + 1; k < sb.bid.length; k++) { + if (sb.bid[k].impid === candidate.impid && sb.bid[k].price > candidate.price) { + candidate = sb.bid[k]; + } + } + retSeatbid.bid.push(candidate); + } + ret.push(retSeatbid); + } + return ret; + }, + getUserSyncs(optionsType, serverResponse, gdprConsent, usPrivacy) { + logInfo('getUserSyncs optionsType', optionsType, 'serverResponse', serverResponse, 'usPrivacy', usPrivacy, 'cookieSyncBag', this.cookieSyncBag); + if (!serverResponse || serverResponse.length === 0) { + return []; + } + if (optionsType.iframeEnabled) { + var arrQueryString = []; + if (config.getConfig('debug')) { + arrQueryString.push('pbjs_debug=true'); + } + arrQueryString.push('usp_consent=' + (usPrivacy || '')); + for (let keyname in this.cookieSyncBag.userIdObject) { + arrQueryString.push(keyname + '=' + this.cookieSyncBag.userIdObject[keyname]); + } + arrQueryString.push('publisherId=' + this.cookieSyncBag.publisherId); + arrQueryString.push('siteId=' + this.cookieSyncBag.siteId); + arrQueryString.push('cb=' + Date.now()); + arrQueryString.push('bidder=' + this.propertyBag.config.bidder); + var strQueryString = arrQueryString.join('&'); + if (strQueryString.length > 0) { + strQueryString = '?' + strQueryString; + } + logInfo('getUserSyncs going to return cookie sync url : ' + this.getCookieSyncUrl() + strQueryString); + return [{ + type: 'iframe', + url: this.getCookieSyncUrl() + strQueryString + }]; + } + }, + getBidRequestForBidId(bidId, arrBids) { + for (let i = 0; i < arrBids.length; i++) { + if (arrBids[i].bidId === bidId) { // bidId in the request comes back as impid in the seatbid bids + return arrBids[i]; + } + } + return null; + }, + findAllUserIds(bidRequest) { + var ret = {}; + let searchKeysSingle = ['pubcid', 'tdid', 'idl_env', 'criteoId', 'lotamePanoramaId', 'fabrickId']; + if (bidRequest.hasOwnProperty('userId')) { + for (let arrayId in searchKeysSingle) { + let key = searchKeysSingle[arrayId]; + if (bidRequest.userId.hasOwnProperty(key)) { + if (typeof (bidRequest.userId[key]) == 'string') { + ret[key] = bidRequest.userId[key]; + } else if (typeof (bidRequest.userId[key]) == 'object') { + logError(`WARNING: findAllUserIds had to use first key in user object to get value for bid.userId key: ${key}. Prebid adapter should be updated.`); + ret[key] = bidRequest.userId[key][Object.keys(bidRequest.userId[key])[0]]; // cannot use Object.values + } else { + logError(`failed to get string key value for userId : ${key}`); + } + } + } + let lipbid = deepAccess(bidRequest.userId, 'lipb.lipbid'); + if (lipbid) { + ret['lipb'] = {'lipbid': lipbid}; + } + let id5id = deepAccess(bidRequest.userId, 'id5id.uid'); + if (id5id) { + ret['id5id'] = id5id; + } + let parrableId = deepAccess(bidRequest.userId, 'parrableId.eid'); + if (parrableId) { + ret['parrableId'] = parrableId; + } + let sharedid = deepAccess(bidRequest.userId, 'sharedid.id'); + if (sharedid) { + ret['sharedid'] = sharedid; + } + } + if (!ret.hasOwnProperty('pubcid')) { + let pubcid = deepAccess(bidRequest, 'crumbs.pubcid'); + if (pubcid) { + ret['pubcid'] = pubcid; // if built with old pubCommonId module + } + } + return ret; + }, + getPlacementId(bidRequest) { + return (bidRequest.params.placementId).toString(); + }, + getPlacementIdOverrideFromGetParam() { + let arr = this.getGetParametersAsObject(); + if (arr.hasOwnProperty('npstoredrequest')) { + if (this.isValidPlacementId(arr['npstoredrequest'])) { + logInfo(`using GET npstoredrequest ` + arr['npstoredrequest'] + ' to replace placementId'); + return arr['npstoredrequest']; + } else { + logError(`GET npstoredrequest FAILED VALIDATION - will not use it`); + } + } + return null; + }, + getGetParametersAsObject() { + let parsed = parseUrl(getRefererInfo().page); + logInfo('getGetParametersAsObject found:', parsed.search); + return parsed.search; + }, + blockTheRequest() { + let npRequest = config.getConfig('newspassid.np_request'); + if (typeof npRequest == 'boolean' && !npRequest) { + logWarn(`Will not allow auction : np_request is set to false`); + return true; + } + return false; + }, + getPageId: function() { + if (this.propertyBag.pageId == null) { + let randPart = ''; + let allowable = '0123456789abcdefghijklmnopqrstuvwxyz'; + for (let i = 20; i > 0; i--) { + randPart += allowable[Math.floor(Math.random() * 36)]; + } + this.propertyBag.pageId = new Date().getTime() + '_' + randPart; + } + return this.propertyBag.pageId; + }, + addStandardProperties(seatBid, defaultWidth, defaultHeight) { + seatBid.cpm = seatBid.price; + seatBid.bidId = seatBid.impid; + seatBid.requestId = seatBid.impid; + seatBid.width = seatBid.w || defaultWidth; + seatBid.height = seatBid.h || defaultHeight; + seatBid.ad = seatBid.adm; + seatBid.netRevenue = true; + seatBid.creativeId = seatBid.crid; + seatBid.currency = 'USD'; + seatBid.ttl = 300; + return seatBid; + }, + getWinnerForRequestBid(requestBidId, serverResponseSeatBid) { + let thisBidWinner = null; + let winningSeat = null; + for (let j = 0; j < serverResponseSeatBid.length; j++) { + let theseBids = serverResponseSeatBid[j].bid; + let thisSeat = serverResponseSeatBid[j].seat; + for (let k = 0; k < theseBids.length; k++) { + if (theseBids[k].impid === requestBidId) { + if ((thisBidWinner == null) || (thisBidWinner.price < theseBids[k].price)) { + thisBidWinner = theseBids[k]; + winningSeat = thisSeat; + break; + } + } + } + } + return {'seat': winningSeat, 'bid': thisBidWinner}; + }, + getAllBidsForBidId(matchBidId, serverResponseSeatBid) { + let objBids = {}; + for (let j = 0; j < serverResponseSeatBid.length; j++) { + let theseBids = serverResponseSeatBid[j].bid; + let thisSeat = serverResponseSeatBid[j].seat; + for (let k = 0; k < theseBids.length; k++) { + if (theseBids[k].impid === matchBidId) { + if (objBids.hasOwnProperty(thisSeat)) { // > 1 bid for an adunit from a bidder - only use the one with the highest bid + if (objBids[thisSeat]['price'] < theseBids[k].price) { + objBids[thisSeat] = theseBids[k]; + } + } else { + objBids[thisSeat] = theseBids[k]; + } + } + } + } + return objBids; + } +}; +export function injectAdIdsIntoAllBidResponses(seatbid) { + logInfo('injectAdIdsIntoAllBidResponses', seatbid); + for (let i = 0; i < seatbid.length; i++) { + let sb = seatbid[i]; + for (let j = 0; j < sb.bid.length; j++) { + sb.bid[j]['adId'] = `${sb.bid[j]['impid']}-${i}-np-${j}`; + } + } + return seatbid; +} +export function checkDeepArray(Arr) { + if (Array.isArray(Arr)) { + if (Array.isArray(Arr[0])) { + return Arr[0]; + } else { + return Arr; + } + } else { + return Arr; + } +} +export function defaultSize(thebidObj) { + if (!thebidObj) { + logInfo('defaultSize received empty bid obj! going to return fixed default size'); + return { + 'defaultHeight': 250, + 'defaultWidth': 300 + }; + } + const {sizes} = thebidObj; + const returnObject = {}; + returnObject.defaultWidth = checkDeepArray(sizes)[0]; + returnObject.defaultHeight = checkDeepArray(sizes)[1]; + return returnObject; +} +export function getRoundedBid(price, mediaType) { + const mediaTypeGranularity = config.getConfig(`mediaTypePriceGranularity.${mediaType}`); // might be string or object or nothing; if set then this takes precedence over 'priceGranularity' + let objBuckets = config.getConfig('customPriceBucket'); // this is always an object - {} if strBuckets is not 'custom' + let strBuckets = config.getConfig('priceGranularity'); // priceGranularity value, always a string ** if priceGranularity is set to an object then it's always 'custom' ** + let theConfigObject = getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets); + let theConfigKey = getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets); + logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); + let priceStringsObj = getPriceBucketString( + price, + theConfigObject, + config.getConfig('currency.granularityMultiplier') + ); + logInfo('priceStringsObj', priceStringsObj); + let granularityNamePriceStringsKeyMapping = { + 'medium': 'med', + 'custom': 'custom', + 'high': 'high', + 'low': 'low', + 'dense': 'dense' + }; + if (granularityNamePriceStringsKeyMapping.hasOwnProperty(theConfigKey)) { + let priceStringsKey = granularityNamePriceStringsKeyMapping[theConfigKey]; + logInfo('getRoundedBid: looking for priceStringsKey:', priceStringsKey); + return priceStringsObj[priceStringsKey]; + } + return priceStringsObj['auto']; +} +export function getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets) { + if (typeof mediaTypeGranularity === 'string') { + return mediaTypeGranularity; + } + if (typeof mediaTypeGranularity === 'object') { + return 'custom'; + } + if (typeof strBuckets === 'string') { + return strBuckets; + } + return 'auto'; // fall back to a default key - should literally never be needed. +} +export function getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets) { + if (typeof mediaTypeGranularity === 'object') { + return mediaTypeGranularity; + } + if (strBuckets === 'custom') { + return objBuckets; + } + return ''; +} +registerBidder(spec); +logInfo(`*BidAdapter ${NEWSPASSVERSION} was loaded`); diff --git a/test/spec/modules/newspassidBidAdapter_spec.js b/test/spec/modules/newspassidBidAdapter_spec.js new file mode 100644 index 00000000000..bec6eea7bf2 --- /dev/null +++ b/test/spec/modules/newspassidBidAdapter_spec.js @@ -0,0 +1,1793 @@ +import { expect } from 'chai'; +import { spec, defaultSize } from 'modules/newspassidBidAdapter.js'; +import { config } from 'src/config.js'; +import {getGranularityKeyName, getGranularityObject} from '../../../modules/newspassidBidAdapter.js'; +import * as utils from '../../../src/utils.js'; +const NEWSPASSURI = 'https://bidder.newspassid.com/openrtb2/auction'; +const BIDDER_CODE = 'newspassid'; +var validBidRequests = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsNoCustomData = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsMulti = [ + { + testId: 1, + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + }, + { + testId: 2, + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff0', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c0', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsWithUserIdData = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87', + userId: { + 'pubcid': '12345678', + 'tdid': '1111tdid', + 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, + 'criteoId': '1111criteoId', + 'idl_env': 'liverampId', + 'lipb': {'lipbid': 'lipbidId123'}, + 'parrableId': {'eid': '01.5678.parrableid'}, + 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} + }, + userIdAsEids: [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '12345678', + 'atype': 1 + } + ] + }, + { + 'source': 'adserver.org', + 'uids': [{ + 'id': '1111tdid', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }, + { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-someId', + 'atype': 1, + }] + }, + { + 'source': 'criteoId', + 'uids': [{ + 'id': '1111criteoId', + 'atype': 1, + }] + }, + { + 'source': 'idl_env', + 'uids': [{ + 'id': 'liverampId', + 'atype': 1, + }] + }, + { + 'source': 'lipb', + 'uids': [{ + 'id': {'lipbid': 'lipbidId123'}, + 'atype': 1, + }] + }, + { + 'source': 'parrableId', + 'uids': [{ + 'id': {'eid': '01.5678.parrableid'}, + 'atype': 1, + }] + } + ] + } +]; +var validBidRequestsMinimal = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + params: { publisherId: '9876abcd12-3', placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsNoSizes = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsWithBannerMediaType = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + mediaTypes: {banner: {sizes: [[300, 250], [300, 600]]}}, + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsIsThisCamelCaseEnough = [ + { + 'bidder': 'newspassid', + 'testname': 'validBidRequestsIsThisCamelCaseEnough', + 'params': { + 'publisherId': 'newspassRUP0001', + 'placementId': '8000000009', + 'siteId': '4204204201', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' + ], + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'penl4dfdk', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } + } + ], + 'userId': { + 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' + }, + 'userIdAsEids': [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56', + 'atype': 1 + } + ] + } + ] + }, + mediaTypes: {banner: {sizes: [[300, 250], [300, 600]]}}, + 'adUnitCode': 'some-ad', + 'transactionId': '02c1ea7d-0bf2-451b-a122-1420040d1cf8', + 'bidId': '2899ec066a91ff8', + 'bidderRequestId': '1c1586b27a1b5c8', + 'auctionId': '0456c9b7-5ab2-4fec-9e10-f418d3d1f04c', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } +]; +var validBidderRequest = { + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + auctionStart: 1536838908986, + bidderCode: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + bids: [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'newspassid', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + }], + doneCbCallCount: 1, + start: 1536838908987, + timeout: 3000 +}; +var emptyObject = {}; +var validResponse = { + 'body': { + 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', + 'seatbid': [ + { + 'bid': [ + { + 'id': '677903815252395017', + 'impid': '2899ec066a91ff8', + 'price': 0.5, + 'adm': '', + 'adid': '98493581', + 'adomain': [ + 'http://prebid.org' + ], + 'iurl': 'https://fra1-ib.adnxs.com/cr?id=98493581', + 'cid': '9325', + 'crid': '98493581', + 'cat': [ + 'IAB3-1' + ], + 'w': 300, + 'h': 600, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 555545, + 'auction_id': 6500448734132353000, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } + ], + 'seat': 'appnexus' + } + ], + 'cur': 'GBP', /* NOTE - this is where cur is, not in the seatbids. */ + 'ext': { + 'responsetimemillis': { + 'appnexus': 47, + 'openx': 30 + } + }, + 'timing': { + 'start': 1536848078.089177, + 'end': 1536848078.142203, + 'TimeTaken': 0.05302619934082031 + } + }, + 'headers': {} +}; +var validResponse2BidsSameAdunit = { + 'body': { + 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', + 'seatbid': [ + { + 'bid': [ + { + 'id': '677903815252395017', + 'impid': '2899ec066a91ff8', + 'price': 0.5, + 'adm': '', + 'adid': '98493581', + 'adomain': [ + 'http://prebid.org' + ], + 'iurl': 'https://fra1-ib.adnxs.com/cr?id=98493581', + 'cid': '9325', + 'crid': '98493581', + 'cat': [ + 'IAB3-1' + ], + 'w': 300, + 'h': 600, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 555545, + 'auction_id': 6500448734132353000, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + }, + { + 'id': '677903815252395010', + 'impid': '2899ec066a91ff8', + 'price': 0.9, + 'adm': '', + 'adid': '98493580', + 'adomain': [ + 'http://prebid.org' + ], + 'iurl': 'https://fra1-ib.adnxs.com/cr?id=98493581', + 'cid': '9320', + 'crid': '98493580', + 'cat': [ + 'IAB3-1' + ], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 555540, + 'auction_id': 6500448734132353000, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } ], + 'seat': 'npappnexus' + } + ], + 'cur': 'GBP', /* NOTE - this is where cur is, not in the seatbids. */ + 'ext': { + 'responsetimemillis': { + 'appnexus': 47, + 'openx': 30 + } + }, + 'timing': { + 'start': 1536848078.089177, + 'end': 1536848078.142203, + 'TimeTaken': 0.05302619934082031 + } + }, + 'headers': {} +}; +var validBidResponse1adWith2Bidders = { + 'body': { + 'id': '91221f96-b931-4acc-8f05-c2a1186fa5ac', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', + 'impid': '2899ec066a91ff8', + 'price': 0.36754, + 'adm': '', + 'adid': '134928661', + 'adomain': [ + 'somecompany.com' + ], + 'iurl': 'https:\/\/ams1-ib.adnxs.com\/cr?id=134928661', + 'cid': '8825', + 'crid': '134928661', + 'cat': [ + 'IAB8-15', + 'IAB8-16', + 'IAB8-4', + 'IAB8-1', + 'IAB8-14', + 'IAB8-6', + 'IAB8-13', + 'IAB8-3', + 'IAB8-17', + 'IAB8-12', + 'IAB8-8', + 'IAB8-7', + 'IAB8-2', + 'IAB8-9', + 'IAB8', + 'IAB8-11' + ], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 14640, + 'auction_id': 1.8369641905139e+18, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } + ], + 'seat': 'appnexus' + }, + { + 'bid': [ + { + 'id': '75665207-a1ca-49db-ba0e-a5e9c7d26f32', + 'impid': '37fff511779365a', + 'price': 1.046, + 'adm': '
removed
', + 'adomain': [ + 'kx.com' + ], + 'crid': '13005', + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + } + } + } + ], + 'seat': 'openx' + } + ], + 'ext': { + 'responsetimemillis': { + 'appnexus': 91, + 'openx': 109, + 'npappnexus': 46, + 'npbeeswax': 2, + 'pangaea': 91 + } + } + }, + 'headers': {} +}; +var multiRequest1 = [ + { + 'bidder': 'newspassid', + 'params': { + 'publisherId': 'newspassRUP0001', + 'siteId': '4204204201', + 'placementId': '0420420421', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' + ], + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'penl4dfdk', + 'uayf5jmv3', + 't8nyiude5', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } + } + ] + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'mpu', + 'transactionId': '6480bac7-31b5-4723-9145-ad8966660651', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '2d30e86db743a8', + 'bidderRequestId': '1d03a1dfc563fc', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }, + { + 'bidder': 'newspassid', + 'params': { + 'publisherId': 'newspassRUP0001', + 'siteId': '4204204201', + 'placementId': '0420420421', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' + ], + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'penl4dfdk', + 't8nxz6qzd', + 't8nyiude5', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } + } + ] + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 728, + 90 + ], + [ + 970, + 250 + ] + ] + } + }, + 'adUnitCode': 'leaderboard', + 'transactionId': 'a49988e6-ae7c-46c4-9598-f18db49892a0', + 'sizes': [ + [ + 728, + 90 + ], + [ + 970, + 250 + ] + ], + 'bidId': '3025f169863b7f8', + 'bidderRequestId': '1d03a1dfc563fc', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } +]; +var multiBidderRequest1 = { + bidderRequest: { + 'bidderCode': 'newspassid', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'bidderRequestId': '1d03a1dfc563fc', + 'bids': [ + { + 'bidder': 'newspassid', + 'params': { + 'publisherId': 'newspassRUP0001', + 'siteId': '4204204201', + 'placementId': '0420420421', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' + ], + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'txeh7uyo0', + 't8nxz6qzd', + 't8nyiude5', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } + } + ] + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'mpu', + 'transactionId': '6480bac7-31b5-4723-9145-ad8966660651', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '2d30e86db743a8', + 'bidderRequestId': '1d03a1dfc563fc', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }, + { + 'bidder': 'newspassid', + 'params': { + 'publisherId': 'newspassRUP0001', + 'siteId': '4204204201', + 'placementId': '0420420421', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' + ], + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'penl4dfdk', + 't8nxz6qzd', + 't8nyiude5', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } + } + ] + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 728, + 90 + ], + [ + 970, + 250 + ] + ] + } + }, + 'adUnitCode': 'leaderboard', + 'transactionId': 'a49988e6-ae7c-46c4-9598-f18db49892a0', + 'sizes': [ + [ + 728, + 90 + ], + [ + 970, + 250 + ] + ], + 'bidId': '3025f169863b7f8', + 'bidderRequestId': '1d03a1dfc563fc', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1592918645574, + 'timeout': 3000, + 'refererInfo': { + 'referer': 'http://some.referrer.com', + 'reachedTop': true, + 'numIframes': 0, + 'stack': [ + 'http://some.referrer.com' + ] + }, + 'start': 1592918645578 + } +}; +var multiResponse1 = { + 'body': { + 'id': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'seatbid': [ + { + 'bid': [ + { + 'id': '4419718600113204943', + 'impid': '2d30e86db743a8', + 'price': 0.2484, + 'adm': '', + 'adid': '119683582', + 'adomain': [ + 'https://someurl.com' + ], + 'iurl': 'https://ams1-ib.adnxs.com/cr?id=119683582', + 'cid': '9979', + 'crid': '119683582', + 'cat': [ + 'IAB3' + ], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'newspassid': {}, + 'appnexus': { + 'brand_id': 734921, + 'auction_id': 2995348111857539600, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + }, + 'cpm': 0.2484, + 'bidId': '2d30e86db743a8', + 'requestId': '2d30e86db743a8', + 'width': 300, + 'height': 250, + 'ad': '', + 'netRevenue': true, + 'creativeId': '119683582', + 'currency': 'USD', + 'ttl': 300, + 'originalCpm': 0.2484, + 'originalCurrency': 'USD' + }, + { + 'id': '18552976939844681', + 'impid': '3025f169863b7f8', + 'price': 0.0621, + 'adm': '', + 'adid': '120179216', + 'adomain': [ + 'appnexus.com' + ], + 'iurl': 'https://ams1-ib.adnxs.com/cr?id=120179216', + 'cid': '9979', + 'crid': '120179216', + 'w': 970, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'newspassid': {}, + 'appnexus': { + 'brand_id': 1, + 'auction_id': 3449036134472542700, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + }, + 'cpm': 0.0621, + 'bidId': '3025f169863b7f8', + 'requestId': '3025f169863b7f8', + 'width': 970, + 'height': 250, + 'ad': '', + 'netRevenue': true, + 'creativeId': '120179216', + 'currency': 'USD', + 'ttl': 300, + 'originalCpm': 0.0621, + 'originalCurrency': 'USD' + }, + { + 'id': '18552976939844999', + 'impid': '3025f169863b7f8', + 'price': 0.521, + 'adm': '', + 'adid': '120179216', + 'adomain': [ + 'appnexus.com' + ], + 'iurl': 'https://ams1-ib.adnxs.com/cr?id=120179216', + 'cid': '9999', + 'crid': '120179299', + 'w': 728, + 'h': 90, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'newspassid': {}, + 'appnexus': { + 'brand_id': 1, + 'auction_id': 3449036134472542700, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + }, + 'cpm': 0.521, + 'bidId': '3025f169863b7f8', + 'requestId': '3025f169863b7f8', + 'width': 728, + 'height': 90, + 'ad': '', + 'netRevenue': true, + 'creativeId': '120179299', + 'currency': 'USD', + 'ttl': 300, + 'originalCpm': 0.0621, + 'originalCurrency': 'USD' + } + ], + 'seat': 'npappnexus' + }, + { + 'bid': [ + { + 'id': '1c605e8a-4992-4ec6-8a5c-f82e2938c2db', + 'impid': '2d30e86db743a8', + 'price': 0.01, + 'adm': '
', + 'crid': '540463358', + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'newspassid': {} + } + }, + 'cpm': 0.01, + 'bidId': '2d30e86db743a8', + 'requestId': '2d30e86db743a8', + 'width': 300, + 'height': 250, + 'ad': '
', + 'netRevenue': true, + 'creativeId': '540463358', + 'currency': 'USD', + 'ttl': 300, + 'originalCpm': 0.01, + 'originalCurrency': 'USD' + }, + { + 'id': '3edeb4f7-d91d-44e2-8aeb-4a2f6d295ce5', + 'impid': '3025f169863b7f8', + 'price': 0.01, + 'adm': '
', + 'crid': '540221061', + 'w': 970, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'newspassid': {} + } + }, + 'cpm': 0.01, + 'bidId': '3025f169863b7f8', + 'requestId': '3025f169863b7f8', + 'width': 970, + 'height': 250, + 'ad': '
', + 'netRevenue': true, + 'creativeId': '540221061', + 'currency': 'USD', + 'ttl': 300, + 'originalCpm': 0.01, + 'originalCurrency': 'USD' + } + ], + 'seat': 'openx' + } + ], + 'ext': { + 'debug': {}, + 'responsetimemillis': { + 'beeswax': 6, + 'openx': 91, + 'npappnexus': 40, + 'npbeeswax': 6 + } + } + }, + 'headers': {} +}; +describe('newspassid Adapter', function () { + describe('isBidRequestValid', function () { + let validBidReq = { + bidder: BIDDER_CODE, + params: { + placementId: '1310000099', + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(validBidReq)).to.equal(true); + }); + var validBidReq2 = { + bidder: BIDDER_CODE, + params: { + placementId: '1310000099', + publisherId: '9876abcd12-3', + siteId: '1234567890', + customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}] + }, + siteId: 1234567890 + } + it('should return true when required params found and all optional params are valid', function () { + expect(spec.isBidRequestValid(validBidReq2)).to.equal(true); + }); + var xEmptyPlacement = { + bidder: BIDDER_CODE, + params: { + placementId: '', + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + it('should not validate empty placementId', function () { + expect(spec.isBidRequestValid(xEmptyPlacement)).to.equal(false); + }); + var xMissingPlacement = { + bidder: BIDDER_CODE, + params: { + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + it('should not validate missing placementId', function () { + expect(spec.isBidRequestValid(xMissingPlacement)).to.equal(false); + }); + var xBadPlacement = { + bidder: BIDDER_CODE, + params: { + placementId: '123X45', + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + it('should not validate placementId with a non-numeric value', function () { + expect(spec.isBidRequestValid(xBadPlacement)).to.equal(false); + }); + var xBadPlacementTooShort = { + bidder: BIDDER_CODE, + params: { + placementId: 123456789, /* should be exactly 10 chars */ + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + it('should not validate placementId with a numeric value of wrong length', function () { + expect(spec.isBidRequestValid(xBadPlacementTooShort)).to.equal(false); + }); + var xBadPlacementTooLong = { + bidder: BIDDER_CODE, + params: { + placementId: 12345678901, /* should be exactly 10 chars */ + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + it('should not validate placementId with a numeric value of wrong length', function () { + expect(spec.isBidRequestValid(xBadPlacementTooLong)).to.equal(false); + }); + var xMissingPublisher = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + siteId: '1234567890' + } + }; + it('should not validate missing publisherId', function () { + expect(spec.isBidRequestValid(xMissingPublisher)).to.equal(false); + }); + var xMissingSiteId = { + bidder: BIDDER_CODE, + params: { + publisherId: '9876abcd12-3', + placementId: '1234567890', + } + }; + it('should not validate missing sitetId', function () { + expect(spec.isBidRequestValid(xMissingSiteId)).to.equal(false); + }); + var xBadPublisherTooShort = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '9876abcd12a', + siteId: '1234567890' + } + }; + it('should not validate publisherId being too short', function () { + expect(spec.isBidRequestValid(xBadPublisherTooShort)).to.equal(false); + }); + var xBadPublisherTooLong = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '9876abcd12abc', + siteId: '1234567890' + } + }; + it('should not validate publisherId being too long', function () { + expect(spec.isBidRequestValid(xBadPublisherTooLong)).to.equal(false); + }); + var publisherNumericOk = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: 123456789012, + siteId: '1234567890' + } + }; + it('should validate publisherId being 12 digits', function () { + expect(spec.isBidRequestValid(publisherNumericOk)).to.equal(true); + }); + var xEmptyPublisher = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '', + siteId: '1234567890' + } + }; + it('should not validate empty publisherId', function () { + expect(spec.isBidRequestValid(xEmptyPublisher)).to.equal(false); + }); + var xBadSite = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '9876abcd12-3', + siteId: '12345Z' + } + }; + it('should not validate bad siteId', function () { + expect(spec.isBidRequestValid(xBadSite)).to.equal(false); + }); + it('should not validate siteId too long', function () { + expect(spec.isBidRequestValid(xBadSite)).to.equal(false); + }); + it('should not validate siteId too short', function () { + expect(spec.isBidRequestValid(xBadSite)).to.equal(false); + }); + var allNonStrings = { + bidder: BIDDER_CODE, + params: { + placementId: 1234567890, + publisherId: '9876abcd12-3', + siteId: 1234567890 + } + }; + it('should validate all numeric values being sent as non-string numbers', function () { + expect(spec.isBidRequestValid(allNonStrings)).to.equal(true); + }); + var emptySiteId = { + bidder: BIDDER_CODE, + params: { + placementId: 1234567890, + publisherId: '9876abcd12-3', + siteId: '' + } + }; + it('should not validate siteId being empty string (it is required now)', function () { + expect(spec.isBidRequestValid(emptySiteId)).to.equal(false); + }); + var xBadCustomData = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'siteId': '1234567890', + 'customData': 'this aint gonna work' + } + }; + it('should not validate customData not being an array', function () { + expect(spec.isBidRequestValid(xBadCustomData)).to.equal(false); + }); + var xBadCustomDataOldCustomdataValue = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'siteId': '1234567890', + 'customData': {'gender': 'bart', 'age': 'low'} + } + }; + it('should not validate customData being an object, not an array', function () { + expect(spec.isBidRequestValid(xBadCustomDataOldCustomdataValue)).to.equal(false); + }); + var xBadCustomDataZerocd = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1111111110', + 'publisherId': '9876abcd12-3', + 'siteId': '1234567890', + 'customData': [] + } + }; + it('should not validate customData array having no elements', function () { + expect(spec.isBidRequestValid(xBadCustomDataZerocd)).to.equal(false); + }); + var xBadCustomDataNotargeting = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'customData': [{'settings': {}, 'xx': {'gender': 'bart', 'age': 'low'}}], + siteId: '1234567890' + } + }; + it('should not validate customData[] having no "targeting"', function () { + expect(spec.isBidRequestValid(xBadCustomDataNotargeting)).to.equal(false); + }); + var xBadCustomDataTgtNotObj = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'customData': [{'settings': {}, 'targeting': 'this should be an object'}], + siteId: '1234567890' + } + }; + it('should not validate customData[0].targeting not being an object', function () { + expect(spec.isBidRequestValid(xBadCustomDataTgtNotObj)).to.equal(false); + }); + var xBadCustomParams = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'siteId': '1234567890', + 'customParams': 'this key is no longer valid' + } + }; + it('should not validate customParams - this is a renamed key', function () { + expect(spec.isBidRequestValid(xBadCustomParams)).to.equal(false); + }); + }); + describe('buildRequests', function () { + it('sends bid request to NEWSPASSURI via POST', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.url).to.equal(NEWSPASSURI); + expect(request.method).to.equal('POST'); + }); + it('sends data as a string', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.data).to.be.a('string'); + }); + it('sends all bid parameters', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + it('adds all parameters inside the ext object only', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.data).to.be.a('string'); + var data = JSON.parse(request.data); + expect(data.imp[0].ext.newspassid.customData).to.be.an('array'); + expect(request).not.to.have.key('lotameData'); + expect(request).not.to.have.key('customData'); + }); + it('adds all parameters inside the ext object only - lightning', function () { + let localBidReq = JSON.parse(JSON.stringify(validBidRequests)); + const request = spec.buildRequests(localBidReq, validBidderRequest); + expect(request.data).to.be.a('string'); + var data = JSON.parse(request.data); + expect(data.imp[0].ext.newspassid.customData).to.be.an('array'); + expect(request).not.to.have.key('lotameData'); + expect(request).not.to.have.key('customData'); + }); + it('has correct bidder', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.bidderRequest.bids[0].bidder).to.equal(BIDDER_CODE); + }); + it('handles mediaTypes element correctly', function () { + const request = spec.buildRequests(validBidRequestsWithBannerMediaType, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + it('handles no newspassid or custom data', function () { + const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + it('should not crash when there is no sizes element at all', function () { + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + it('should be able to handle non-single requests', function () { + config.setConfig({'newspassid': {'singleRequest': false}}); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); + expect(request).to.be.a('array'); + expect(request[0]).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + config.setConfig({'newspassid': {'singleRequest': true}}); + }); + it('should not have imp[N].ext.newspassid.userId', function () { + let bidderRequest = validBidderRequest; + let bidRequests = validBidRequests; + bidRequests[0]['userId'] = { + 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, + 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, + 'idl_env': '3333', + 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', + 'pubcid': '5555', + 'tdid': '6666', + 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} + }; + bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + let firstBid = payload.imp[0].ext.newspassid; + expect(firstBid).to.not.have.property('userId'); + delete validBidRequests[0].userId; // tidy up now, else it will screw with other tests + }); + it('should pick up the value of pubcid when built using the pubCommonId module (not userId)', function () { + let bidRequests = validBidRequests; + bidRequests[0]['userId'] = { + 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, + 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, + 'idl_env': '3333', + 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', + 'tdid': '6666', + 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} + }; + bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; + const request = spec.buildRequests(bidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + expect(payload.ext.newspassid.pubcid).to.equal(bidRequests[0]['crumbs']['pubcid']); + delete validBidRequests[0].userId; // tidy up now, else it will screw with other tests + }); + it('should add a user.ext.eids object to contain user ID data in the new location (Nov 2019) Updated Aug 2020', function() { + const request = spec.buildRequests(validBidRequestsWithUserIdData, validBidderRequest); + const payload = JSON.parse(request.data); + expect(payload.user).to.exist; + expect(payload.user.ext).to.exist; + expect(payload.user.ext.eids).to.exist; + expect(payload.user.ext.eids[0]['source']).to.equal('pubcid.org'); + expect(payload.user.ext.eids[0]['uids'][0]['id']).to.equal('12345678'); + expect(payload.user.ext.eids[1]['source']).to.equal('adserver.org'); + expect(payload.user.ext.eids[1]['uids'][0]['id']).to.equal('1111tdid'); + expect(payload.user.ext.eids[2]['source']).to.equal('id5-sync.com'); + expect(payload.user.ext.eids[2]['uids'][0]['id']).to.equal('ID5-someId'); + expect(payload.user.ext.eids[3]['source']).to.equal('criteoId'); + expect(payload.user.ext.eids[3]['uids'][0]['id']).to.equal('1111criteoId'); + expect(payload.user.ext.eids[4]['source']).to.equal('idl_env'); + expect(payload.user.ext.eids[4]['uids'][0]['id']).to.equal('liverampId'); + expect(payload.user.ext.eids[5]['source']).to.equal('lipb'); + expect(payload.user.ext.eids[5]['uids'][0]['id']['lipbid']).to.equal('lipbidId123'); + expect(payload.user.ext.eids[6]['source']).to.equal('parrableId'); + expect(payload.user.ext.eids[6]['uids'][0]['id']['eid']).to.equal('01.5678.parrableid'); + }); + it('replaces the auction url for a config override', function () { + spec.propertyBag.config = null; + let fakeOrigin = 'http://sometestendpoint'; + config.setConfig({'newspassid': {'endpointOverride': {'origin': fakeOrigin}}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.url).to.equal(fakeOrigin + '/openrtb2/auction'); + expect(request.method).to.equal('POST'); + const data = JSON.parse(request.data); + expect(data.ext.newspassid.origin).to.equal(fakeOrigin); + config.setConfig({'newspassid': {'kvpPrefix': null, 'endpointOverride': null}}); + }); + it('replaces the FULL auction url for a config override', function () { + spec.propertyBag.config = null; + let fakeurl = 'http://sometestendpoint/myfullurl'; + config.setConfig({'newspassid': {'endpointOverride': {'auctionUrl': fakeurl}}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.url).to.equal(fakeurl); + expect(request.method).to.equal('POST'); + const data = JSON.parse(request.data); + expect(data.ext.newspassid.origin).to.equal(fakeurl); + config.setConfig({'newspassid': {'kvpPrefix': null, 'endpointOverride': null}}); + }); + it('should ignore kvpPrefix', function () { + spec.propertyBag.config = null; + config.setConfig({'newspassid': {'kvpPrefix': 'np'}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(result[0].adserverTargeting).to.have.own.property('np_appnexus_crid'); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_appnexus_crid')).to.equal('98493581'); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_adId')).to.equal('2899ec066a91ff8-0-np-0'); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_size')).to.equal('300x600'); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_pb_r')).to.equal('0.50'); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_bid')).to.equal('true'); + config.resetConfig(); + }); + it('should create a meta object on each bid returned', function () { + spec.propertyBag.config = null; + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(result[0]).to.have.own.property('meta'); + expect(result[0].meta.advertiserDomains[0]).to.equal('http://prebid.org'); + config.resetConfig(); + }); + it('should use nptestmode GET value if set', function() { + var specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {'nptestmode': 'mytestvalue_123'}; + }; + const request = specMock.buildRequests(validBidRequests, validBidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].ext.newspassid.customData).to.be.an('array'); + expect(data.imp[0].ext.newspassid.customData[0].targeting.nptestmode).to.equal('mytestvalue_123'); + }); + it('should pass through GET params if present: npf, nppf, nprp, npip', function() { + var specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {npf: '1', nppf: '0', nprp: '2', npip: '123'}; + }; + const request = specMock.buildRequests(validBidRequests, validBidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.newspassid.npf).to.equal(1); + expect(data.ext.newspassid.nppf).to.equal(0); + expect(data.ext.newspassid.nprp).to.equal(2); + expect(data.ext.newspassid.npip).to.equal(123); + }); + it('should pass through GET params if present: npf, nppf, nprp, npip with alternative values', function() { + var specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {npf: 'false', nppf: 'true', nprp: 'xyz', npip: 'hello'}; + }; + const request = specMock.buildRequests(validBidRequests, validBidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.newspassid.npf).to.equal(0); + expect(data.ext.newspassid.nppf).to.equal(1); + expect(data.ext.newspassid).to.not.haveOwnProperty('nprp'); + expect(data.ext.newspassid).to.not.haveOwnProperty('npip'); + }); + it('should use nptestmode GET value if set, even if there is no customdata in config', function() { + var specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {'nptestmode': 'mytestvalue_123'}; + }; + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].ext.newspassid.customData).to.be.an('array'); + expect(data.imp[0].ext.newspassid.customData[0].targeting.nptestmode).to.equal('mytestvalue_123'); + }); + it('should use GET values auction=[encoded URL] & cookiesync=[encoded url] if set', function() { + spec.propertyBag.config = null; + var specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {}; + }; + let request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); + let url = request.url; + expect(url).to.equal('https://bidder.newspassid.com/openrtb2/auction'); + let cookieUrl = specMock.getCookieSyncUrl(); + expect(cookieUrl).to.equal('https://bidder.newspassid.com/static/load-cookie.html'); + specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {'auction': 'https://www.someurl.com/auction', 'cookiesync': 'https://www.someurl.com/sync'}; + }; + request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); + url = request.url; + expect(url).to.equal('https://www.someurl.com/auction'); + cookieUrl = specMock.getCookieSyncUrl(); + expect(cookieUrl).to.equal('https://www.someurl.com/sync'); + }); + it('should use a valid npstoredrequest GET value if set to override the placementId values, and set np_rw if we find it', function() { + var specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {'npstoredrequest': '1122334455'}; // 10 digits are valid + }; + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.newspassid.np_rw).to.equal(1); + expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1122334455'); + }); + it('should NOT use an invalid npstoredrequest GET value if set to override the placementId values, and set np_rw to 0', function() { + var specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {'npstoredrequest': 'BADVAL'}; // 10 digits are valid + }; + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.newspassid.np_rw).to.equal(0); + expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1310000099'); + }); + it('should pick up the config value of coppa & set it in the request', function () { + config.setConfig({'coppa': true}); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); + const payload = JSON.parse(request.data); + expect(payload.regs).to.include.keys('coppa'); + expect(payload.regs.coppa).to.equal(1); + config.resetConfig(); + }); + it('should pick up the config value of coppa & only set it in the request if its true', function () { + config.setConfig({'coppa': false}); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); + const payload = JSON.parse(request.data); + expect(utils.deepAccess(payload, 'regs.coppa')).to.be.undefined; + config.resetConfig(); + }); + it('should should contain a unique page view id in the auction request which persists across calls', function () { + let request = spec.buildRequests(validBidRequests, validBidderRequest); + let payload = JSON.parse(request.data); + expect(utils.deepAccess(payload, 'ext.newspassid.pv')).to.be.a('string'); + request = spec.buildRequests(validBidRequestsIsThisCamelCaseEnough, validBidderRequest); + let payload2 = JSON.parse(request.data); + expect(utils.deepAccess(payload2, 'ext.newspassid.pv')).to.be.a('string'); + expect(utils.deepAccess(payload2, 'ext.newspassid.pv')).to.equal(utils.deepAccess(payload, 'ext.newspassid.pv')); + }); + it('should indicate that the whitelist was used when it contains valid data', function () { + config.setConfig({'newspassid': {'np_whitelist_adserver_keys': ['np_appnexus_pb', 'np_appnexus_imp_id']}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + expect(payload.ext.newspassid.np_kvp_rw).to.equal(1); + config.resetConfig(); + }); + it('should indicate that the whitelist was not used when it contains no data', function () { + config.setConfig({'newspassid': {'np_whitelist_adserver_keys': []}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + expect(payload.ext.newspassid.np_kvp_rw).to.equal(0); + config.resetConfig(); + }); + it('should indicate that the whitelist was not used when it is not set in the config', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + expect(payload.ext.newspassid.np_kvp_rw).to.equal(0); + }); + it('should handle ortb2 site data', function () { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { + 'site': { + 'name': 'example_ortb2_name', + 'domain': 'page.example.com', + 'cat': ['IAB2'], + 'sectioncat': ['IAB2-2'], + 'pagecat': ['IAB2-2'], + 'page': 'https://page.example.com/here.html', + 'ref': 'https://ref.example.com', + 'keywords': 'power tools, drills', + 'search': 'drill' + } + }; + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.imp[0].ext.newspassid.customData[0].targeting.name).to.equal('example_ortb2_name'); + expect(payload.user.ext).to.not.have.property('gender'); + }); + it('should add ortb2 site data when there is no customData already created', function () { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { + 'site': { + 'name': 'example_ortb2_name', + 'domain': 'page.example.com', + 'cat': ['IAB2'], + 'sectioncat': ['IAB2-2'], + 'pagecat': ['IAB2-2'], + 'page': 'https://page.example.com/here.html', + 'ref': 'https://ref.example.com', + 'keywords': 'power tools, drills', + 'search': 'drill' + } + }; + const request = spec.buildRequests(validBidRequestsNoCustomData, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.imp[0].ext.newspassid.customData[0].targeting.name).to.equal('example_ortb2_name'); + expect(payload.imp[0].ext.newspassid.customData[0].targeting).to.not.have.property('gender') + }); + it('should add ortb2 user data to the user object', function () { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { + 'user': { + 'gender': 'I identify as a box of rocks' + } + }; + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.user.gender).to.equal('I identify as a box of rocks'); + }); + it('handles schain object in each bidrequest (will be the same in each br)', function () { + let br = JSON.parse(JSON.stringify(validBidRequests)); + let schainConfigObject = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'bidderA.com', + 'sid': '00001', + 'hp': 1 + } + ] + }; + br[0]['schain'] = schainConfigObject; + const request = spec.buildRequests(br, validBidderRequest); + const data = JSON.parse(request.data); + expect(data.source.ext).to.haveOwnProperty('schain'); + expect(data.source.ext.schain).to.deep.equal(schainConfigObject); // .deep.equal() : Target object deeply (but not strictly) equals `{a: 1}` + }); + }); + describe('interpretResponse', function () { + it('should build bid array', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(result.length).to.equal(1); + }); + it('should have all relevant fields', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + const bid = result[0]; + expect(bid.cpm).to.equal(validResponse.body.seatbid[0].bid[0].cpm); + expect(bid.width).to.equal(validResponse.body.seatbid[0].bid[0].width); + expect(bid.height).to.equal(validResponse.body.seatbid[0].bid[0].height); + }); + it('should build bid array with usp/CCPA', function () { + let validBR = JSON.parse(JSON.stringify(validBidderRequest)); + validBR.uspConsent = '1YNY'; + const request = spec.buildRequests(validBidRequests, validBR); + const payload = JSON.parse(request.data); + expect(payload.user.ext.uspConsent).not.to.exist; + expect(payload.regs.ext.us_privacy).to.equal('1YNY'); + }); + it('should fail ok if no seatbid in server response', function () { + const result = spec.interpretResponse({}, {}); + expect(result).to.be.an('array'); + expect(result).to.be.empty; + }); + it('should fail ok if seatbid is not an array', function () { + const result = spec.interpretResponse({'body': {'seatbid': 'nothing_here'}}, {}); + expect(result).to.be.an('array'); + expect(result).to.be.empty; + }); + it('should correctly parse response where there are more bidders than ad slots', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validBidResponse1adWith2Bidders, request); + expect(result.length).to.equal(2); + }); + it('should have a ttl of 600', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(result[0].ttl).to.equal(300); + }); + it('should handle a valid whitelist, removing items not on the list & leaving others', function () { + config.setConfig({'newspassid': {'np_whitelist_adserver_keys': ['np_appnexus_crid', 'np_appnexus_adId']}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_appnexus_adv')).to.be.undefined; + expect(utils.deepAccess(result[0].adserverTargeting, 'np_appnexus_adId')).to.equal('2899ec066a91ff8-0-np-0'); + config.resetConfig(); + }); + it('should ignore a whitelist if enhancedAdserverTargeting is false', function () { + config.setConfig({'newspassid': {'np_whitelist_adserver_keys': ['np_appnexus_crid', 'np_appnexus_imp_id'], 'enhancedAdserverTargeting': false}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_appnexus_adv')).to.be.undefined; + expect(utils.deepAccess(result[0].adserverTargeting, 'np_appnexus_imp_id')).to.be.undefined; + config.resetConfig(); + }); + it('should correctly handle enhancedAdserverTargeting being false', function () { + config.setConfig({'newspassid': {'enhancedAdserverTargeting': false}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_appnexus_adv')).to.be.undefined; + expect(utils.deepAccess(result[0].adserverTargeting, 'np_appnexus_imp_id')).to.be.undefined; + config.resetConfig(); + }); + it('should add unique adId values to each bid', function() { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + let validres = JSON.parse(JSON.stringify(validResponse2BidsSameAdunit)); + const result = spec.interpretResponse(validres, request); + expect(result.length).to.equal(1); + expect(result[0]['price']).to.equal(0.9); + expect(result[0]['adserverTargeting']['np_npappnexus_adId']).to.equal('2899ec066a91ff8-0-np-1'); + }); + it('should correctly process an auction with 2 adunits & multiple bidders one of which bids for both adslots', function() { + let validres = JSON.parse(JSON.stringify(multiResponse1)); + let request = spec.buildRequests(multiRequest1, multiBidderRequest1.bidderRequest); + let result = spec.interpretResponse(validres, request); + expect(result.length).to.equal(4); // one of the 5 bids will have been removed + expect(result[1]['price']).to.equal(0.521); + expect(result[1]['impid']).to.equal('3025f169863b7f8'); + expect(result[1]['id']).to.equal('18552976939844999'); + expect(result[1]['adserverTargeting']['np_npappnexus_adId']).to.equal('3025f169863b7f8-0-np-2'); + validres = JSON.parse(JSON.stringify(multiResponse1)); + validres.body.seatbid[0].bid[1].price = 1.1; + validres.body.seatbid[0].bid[1].cpm = 1.1; + request = spec.buildRequests(multiRequest1, multiBidderRequest1.bidderRequest); + result = spec.interpretResponse(validres, request); + expect(result[1]['price']).to.equal(1.1); + expect(result[1]['impid']).to.equal('3025f169863b7f8'); + expect(result[1]['id']).to.equal('18552976939844681'); + expect(result[1]['adserverTargeting']['np_npappnexus_adId']).to.equal('3025f169863b7f8-0-np-1'); + }); + }); + describe('userSyncs', function () { + it('should fail gracefully if no server response', function () { + const result = spec.getUserSyncs('bad', false, emptyObject); + expect(result).to.be.empty; + }); + it('should fail gracefully if server response is empty', function () { + const result = spec.getUserSyncs('bad', [], emptyObject); + expect(result).to.be.empty; + }); + it('should append the various values if they exist', function() { + spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', emptyObject); + expect(result).to.be.an('array'); + expect(result[0].url).to.include('publisherId=9876abcd12-3'); + expect(result[0].url).to.include('siteId=1234567890'); + }); + it('should append ccpa (usp data)', function() { + spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', emptyObject, '1YYN'); + expect(result).to.be.an('array'); + expect(result[0].url).to.include('usp_consent=1YYN'); + }); + it('should use "" if no usp is sent to cookieSync', function() { + spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', emptyObject); + expect(result).to.be.an('array'); + expect(result[0].url).to.include('usp_consent=&'); + }); + }); + describe('default size', function () { + it('should should return default sizes if no obj is sent', function () { + let obj = ''; + const result = defaultSize(obj); + expect(result.defaultHeight).to.equal(250); + expect(result.defaultWidth).to.equal(300); + }); + }); + describe('getGranularityKeyName', function() { + it('should return a string granularity as-is', function() { + const result = getGranularityKeyName('', 'this is it', ''); + expect(result).to.equal('this is it'); + }); + it('should return "custom" for a mediaTypeGranularity object', function() { + const result = getGranularityKeyName('', {}, ''); + expect(result).to.equal('custom'); + }); + it('should return "custom" for a mediaTypeGranularity object', function() { + const result = getGranularityKeyName('', false, 'string buckets'); + expect(result).to.equal('string buckets'); + }); + }); + describe('getGranularityObject', function() { + it('should return an object as-is', function() { + const result = getGranularityObject('', {'name': 'mark'}, '', ''); + expect(result.name).to.equal('mark'); + }); + it('should return an object as-is', function() { + const result = getGranularityObject('', false, 'custom', {'name': 'rupert'}); + expect(result.name).to.equal('rupert'); + }); + }); + describe('blockTheRequest', function() { + it('should return true if np_request is false', function() { + config.setConfig({'newspassid': {'np_request': false}}); + let result = spec.blockTheRequest(); + expect(result).to.be.true; + config.resetConfig(); + }); + it('should return false if np_request is true', function() { + config.setConfig({'newspassid': {'np_request': true}}); + let result = spec.blockTheRequest(); + expect(result).to.be.false; + config.resetConfig(); + }); + }); + describe('getPageId', function() { + it('should return the same Page ID for multiple calls', function () { + let result = spec.getPageId(); + expect(result).to.be.a('string'); + let result2 = spec.getPageId(); + expect(result2).to.equal(result); + }); + }); + describe('getBidRequestForBidId', function() { + it('should locate a bid inside a bid array', function () { + let result = spec.getBidRequestForBidId('2899ec066a91ff8', validBidRequestsMulti); + expect(result.testId).to.equal(1); + result = spec.getBidRequestForBidId('2899ec066a91ff0', validBidRequestsMulti); + expect(result.testId).to.equal(2); + }); + }); + describe('removeSingleBidderMultipleBids', function() { + it('should remove the multi bid by npappnexus for adslot 2d30e86db743a8', function() { + let validres = JSON.parse(JSON.stringify(multiResponse1)); + expect(validres.body.seatbid[0].bid.length).to.equal(3); + expect(validres.body.seatbid[0].seat).to.equal('npappnexus'); + let response = spec.removeSingleBidderMultipleBids(validres.body.seatbid); + expect(response.length).to.equal(2); + expect(response[0].bid.length).to.equal(2); + expect(response[0].seat).to.equal('npappnexus'); + expect(response[1].bid.length).to.equal(2); + }); + }); +}); From 6f7f56b73374c44e0a6704bcb6bc61b9df644790 Mon Sep 17 00:00:00 2001 From: Muki Seiler Date: Tue, 12 Jul 2022 21:14:59 +0200 Subject: [PATCH 019/569] Add gvlid to ogury (#8680) --- modules/oguryBidAdapter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index 746fc1eec03..932d0d43f0e 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -6,6 +6,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { ajax } from '../src/ajax.js' const BIDDER_CODE = 'ogury'; +const GVLID = 31; const DEFAULT_TIMEOUT = 1000; const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; const TIMEOUT_MONITORING_HOST = 'https://ms-ads-monitoring-events.presage.io'; @@ -198,6 +199,7 @@ function onTimeout(timeoutData) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid, getUserSyncs, From 9af6d0cf34dc032f42619113267e4a43f1b0346e Mon Sep 17 00:00:00 2001 From: Gena Date: Wed, 13 Jul 2022 15:49:56 +0200 Subject: [PATCH 020/569] Update select media gvlid (#8684) --- modules/adtelligentBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 5269b1224c1..6c6fca13d7b 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -37,7 +37,8 @@ const syncsCache = {}; export const spec = { code: BIDDER_CODE, gvlid: 410, - aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa', 'bidsxchange', 'streamkey', 'janet', + aliases: ['onefiftytwomedia', 'appaloosa', 'bidsxchange', 'streamkey', 'janet', + { code: 'selectmedia', gvlid: 775 }, { code: 'navelix', gvlid: 380 }, 'pgam' ], From 0be4560e182e46c121c283f763cdef74b5ff9db9 Mon Sep 17 00:00:00 2001 From: JacobKlein26 <42449375+JacobKlein26@users.noreply.github.com> Date: Wed, 13 Jul 2022 09:53:37 -0400 Subject: [PATCH 021/569] NextMillennium Bid Adapter: Add ortb and element offsets (#8671) * ortb2 and el offsets * return empty object * ortb2 and el offsets * IX Bid Adapter: Add support for impression level transactionId (imp[].ext.tid) (#8641) * IX Bid Adapter: add imp ext tid support * fix tid level in imp ext object Co-authored-by: shahin.rahbariasl * PBjs Core Price Floors Module: improve logging on bid rejections to clarify which CPM is being compared with which floor (#8655) * Price floors module: improve logging on bid rejections to clarify which CPM is being compared with which floor * Use full precision in log message * Adgeneration Bid Adpter: add Criteo system and ID5 system and update test. (#8642) * update Adgeneration to add Criteo system and ID5 systems. * Change method name. * Correction_of_code_and_test_specs. * Adding adgext_id5_id_link_type parameter to ID5 object. * return empty object * fix ortb location (not in ext) * add unit testing Co-authored-by: shahinrahbariasl <56240400+shahinrahbariasl@users.noreply.github.com> Co-authored-by: shahin.rahbariasl Co-authored-by: Demetrio Girardi Co-authored-by: Keisuke Kakinuma --- modules/nextMillenniumBidAdapter.js | 18 ++++++++++++++++- .../modules/nextMillenniumBidAdapter_spec.js | 20 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 91508d38ca0..375258176da 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -35,8 +35,11 @@ export const spec = { 'nextMillennium': { 'refresh_count': window.nmmRefreshCounts[bid.adUnitCode]++, + 'elOffsets': getBoundingClient(bid), + 'scrollTop': window.pageYOffset || document.documentElement.scrollTop } - } + }, + ...bid.ortb2 } const gdprConsent = bidderRequest && bidderRequest.gdprConsent; @@ -132,6 +135,19 @@ export const spec = { }]; }, }; +function getAdEl(bid) { + // best way I could think of to get El, is by matching adUnitCode to google slots... + const slot = window.googletag && window.googletag.pubads && window.googletag.pubads().getSlots().find(slot => slot.getAdUnitPath() === bid.adUnitCode); + const slotElementId = slot && slot.getSlotElementId(); + if (!slotElementId) return null; + return document.querySelector('#' + slotElementId); +} +function getBoundingClient(bid) { + // console.log(bid) + const el = getAdEl(bid) + if (!el) return {} + return el.getBoundingClientRect(); +} function getPlacementId(bid) { const groupId = getBidIdParameter('group_id', bid.params) diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index a8aa62f24d1..9362388b539 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -14,6 +14,16 @@ describe('nextMillenniumBidAdapterTests', function() { gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true + }, + ortb2: { + device: { + w: 1500, + h: 1000 + }, + site: { + domain: 'example.com', + page: 'http://example.com' + } } } ]; @@ -94,6 +104,16 @@ describe('nextMillenniumBidAdapterTests', function() { expect(JSON.parse(request[0].data).ext.nextMillennium.refresh_count).to.equal(3); }); + it('Check if ORTB was added', function() { + const request = spec.buildRequests(bidRequestData) + expect(JSON.parse(request[0].data).site.domain).to.equal('example.com') + }) + + it('Check if elOffsets was added', function() { + const request = spec.buildRequests(bidRequestData) + expect(JSON.parse(request[0].data).ext.nextMillennium.elOffsets).to.be.an('object') + }) + it('Test getUserSyncs function', function () { const syncOptions = { 'iframeEnabled': true From c99e849756f4e6ba88f3417d2ba878d570670539 Mon Sep 17 00:00:00 2001 From: wsusrasp <106743463+wsusrasp@users.noreply.github.com> Date: Thu, 14 Jul 2022 15:54:04 +0200 Subject: [PATCH 022/569] Ras Bid Adapter: Initial release (#8551) * Ras Bid Adapter: Initial release * * Ras Bid Adapter: "du" and "pos" params * * Ras Bid Adapter: "pos" param is not supported * switch to new refererInfo format * * Ras Bid Adapter: "du" and "dr" params Co-authored-by: skoklowski --- modules/rasBidAdapter.js | 149 +++++++++++++++++++ modules/rasBidAdapter.md | 50 +++++++ test/spec/modules/rasBidAdapter_spec.js | 190 ++++++++++++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 modules/rasBidAdapter.js create mode 100644 modules/rasBidAdapter.md create mode 100644 test/spec/modules/rasBidAdapter_spec.js diff --git a/modules/rasBidAdapter.js b/modules/rasBidAdapter.js new file mode 100644 index 00000000000..d9e6b6a9126 --- /dev/null +++ b/modules/rasBidAdapter.js @@ -0,0 +1,149 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { isEmpty, getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; + +const BIDDER_CODE = 'ras'; +const VERSION = '1.0'; + +const getEndpoint = (network) => `https://csr.onet.pl/${encodeURIComponent(network)}/csr-006/csr.json?`; + +function parseParams(params, bidderRequest) { + const newParams = {}; + const du = deepAccess(bidderRequest, 'refererInfo.page'); + const dr = deepAccess(bidderRequest, 'refererInfo.ref'); + + if (du) { + newParams.du = du; + } + if (dr) { + newParams.dr = dr; + } + const pageContext = params.pageContext; + if (!pageContext) { + return newParams; + } + if (pageContext.du) { + newParams.du = pageContext.du; + } + if (pageContext.dr) { + newParams.dr = pageContext.dr; + } + if (pageContext.dv) { + newParams.DV = pageContext.dv; + } + if (pageContext.keyWords && Array.isArray(pageContext.keyWords)) { + newParams.kwrd = pageContext.keyWords.join('+'); + } + if (pageContext.capping) { + newParams.local_capping = pageContext.capping; + } + if (pageContext.keyValues && typeof pageContext.keyValues === 'object') { + for (const param in pageContext.keyValues) { + if (pageContext.keyValues.hasOwnProperty(param)) { + const kvName = 'kv' + param; + newParams[kvName] = pageContext.keyValues[param]; + } + } + } + return newParams; +} + +const buildBid = (ad) => { + if (ad.type === 'empty') { + return null; + } + return { + requestId: ad.id, + cpm: ad.bid_rate ? ad.bid_rate.toFixed(2) : 0, + width: ad.width || 0, + height: ad.height || 0, + ttl: 300, + creativeId: ad.adid ? parseInt(ad.adid.split(',')[2], 10) : 0, + netRevenue: true, + currency: ad.currency || 'USD', + dealId: null, + meta: { + mediaType: BANNER + }, + ad: ad.html || null + }; +}; + +const getContextParams = (bidRequests, bidderRequest) => { + const bid = bidRequests[0]; + const { params } = bid; + const requestParams = { + site: params.site, + area: params.area, + cre_format: 'html', + systems: 'das', + kvprver: VERSION, + ems_url: 1, + bid_rate: 1, + ...parseParams(params, bidderRequest) + }; + return Object.keys(requestParams).map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(requestParams[key])).join('&'); +}; + +const getSlots = (bidRequests) => { + let queryString = ''; + const batchSize = bidRequests.length; + for (let i = 0; i < batchSize; i++) { + const adunit = bidRequests[i]; + const sizes = parseSizesInput(getAdUnitSizes(adunit)).join(','); + queryString += `&slot${i}=${encodeURIComponent(adunit.params.slot)}&id${i}=${encodeURIComponent(adunit.bidId)}&composition${i}=CHILD`; + if (sizes.length) { + queryString += `&iusizes${i}=${encodeURIComponent(sizes)}`; + } + } + return queryString; +}; + +const getGdprParams = (bidderRequest) => { + const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); + let consentString = deepAccess(bidderRequest, 'gdprConsent.consentString'); + let queryString = ''; + if (gdprApplies !== undefined) { + queryString += `&gdpr_applies=${encodeURIComponent(gdprApplies)}`; + } + if (consentString !== undefined) { + queryString += `&euconsent=${encodeURIComponent(consentString)}`; + } + return queryString; +}; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function (bidRequest) { + if (!bidRequest || !bidRequest.params || typeof bidRequest.params !== 'object') { + return; + } + const { params } = bidRequest; + return Boolean(params.network && params.site && params.area && params.slot); + }, + + buildRequests: function (bidRequests, bidderRequest) { + const slotsQuery = getSlots(bidRequests); + const contextQuery = getContextParams(bidRequests, bidderRequest); + const gdprQuery = getGdprParams(bidderRequest); + const bidIds = bidRequests.map((bid) => ({ slot: bid.params.slot, bidId: bid.bidId })); + const network = bidRequests[0].params.network; + return [{ + method: 'GET', + url: getEndpoint(network) + contextQuery + slotsQuery + gdprQuery, + bidIds: bidIds + }]; + }, + + interpretResponse: function (serverResponse, bidRequest) { + const response = serverResponse.body; + if (!response || !response.ads || response.ads.length === 0) { + return []; + } + return response.ads.map(buildBid).filter((bid) => !isEmpty(bid)); + } +}; + +registerBidder(spec); diff --git a/modules/rasBidAdapter.md b/modules/rasBidAdapter.md new file mode 100644 index 00000000000..5cf75c3446d --- /dev/null +++ b/modules/rasBidAdapter.md @@ -0,0 +1,50 @@ +# Overview + +``` +Module Name: Ringier Axel Springer Bidder Adapter +Module Type: Bidder Adapter +Maintainer: support@ringpublishing.com +``` + +# Description + +Module that connects to Ringer Axel Springer demand sources. +Only banner format is supported. + +# Test Parameters +```js +var adUnits = [{ + code: 'test-div-ad', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bids: [{ + bidder: 'ras', + params: { + network: '4178463', + site: 'test', + area: 'areatest', + slot: 'slot' + } + }] +}]; +``` + +# Parameters + +| Name | Scope | Type | Description | Example +| --- | --- | --- | --- | --- +| network | required | String | Specific identifier provided by RAS | `"4178463"` +| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by RAS | `"example_com"` +| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` +| slot | required | String | Ad unit placement name (case-insensitive) provided by RAS | `"slot"` +| pageContext | optional | Object | Web page context data | `{}` +| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` +| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` +| pageContext.dv | optional | String | Document virtual address as slash-separated path that may consist of any number of parts (case-insensitive alphanumeric with underscores and hyphens); first part should be the same as `site` value and second as `area` value; next parts may reflect website navigation | `"example_com/sport/football"` +| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` +| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` +| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` +| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` diff --git a/test/spec/modules/rasBidAdapter_spec.js b/test/spec/modules/rasBidAdapter_spec.js new file mode 100644 index 00000000000..e3f41566c99 --- /dev/null +++ b/test/spec/modules/rasBidAdapter_spec.js @@ -0,0 +1,190 @@ +import { expect } from 'chai'; +import { spec } from 'modules/rasBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +const CSR_ENDPOINT = 'https://csr.onet.pl/4178463/csr-006/csr.json?'; + +describe('rasBidAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + const bid = { + sizes: [[300, 250], [300, 600]], + bidder: 'ras', + params: { + slot: 'slot', + area: 'areatest', + site: 'test', + network: '4178463' + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params not found', function () { + const failBid = { + sizes: [[300, 250], [300, 300]], + bidder: 'ras', + params: { + site: 'test', + network: '4178463' + } + }; + expect(spec.isBidRequestValid(failBid)).to.equal(false); + }); + + it('should return nothing when bid request is malformed', function () { + const failBid = { + sizes: [[300, 250], [300, 300]], + bidder: 'ras', + }; + expect(spec.isBidRequestValid(failBid)).to.equal(undefined); + }); + }); + + describe('buildRequests', function () { + const bid = { + sizes: [[300, 250], [300, 600]], + bidder: 'ras', + bidId: 1, + params: { + slot: 'test', + area: 'areatest', + site: 'test', + network: '4178463' + } + }; + const bid2 = { + sizes: [[750, 300]], + bidder: 'ras', + bidId: 2, + params: { + slot: 'test2', + area: 'areatest', + site: 'test', + network: '4178463' + } + }; + + it('should parse bids to request', function () { + const requests = spec.buildRequests([bid], { + 'gdprConsent': { + 'gdprApplies': true, + 'consentString': 'some-consent-string' + }, + 'refererInfo': { + 'ref': 'https://example.org/', + 'page': 'https://example.com/' + } + }); + expect(requests[0].url).to.have.string(CSR_ENDPOINT); + expect(requests[0].url).to.have.string('slot0=test'); + expect(requests[0].url).to.have.string('id0=1'); + expect(requests[0].url).to.have.string('site=test'); + expect(requests[0].url).to.have.string('area=areatest'); + expect(requests[0].url).to.have.string('cre_format=html'); + expect(requests[0].url).to.have.string('systems=das'); + expect(requests[0].url).to.have.string('ems_url=1'); + expect(requests[0].url).to.have.string('bid_rate=1'); + expect(requests[0].url).to.have.string('gdpr_applies=true'); + expect(requests[0].url).to.have.string('euconsent=some-consent-string'); + expect(requests[0].url).to.have.string('du=https%3A%2F%2Fexample.com%2F'); + expect(requests[0].url).to.have.string('dr=https%3A%2F%2Fexample.org%2F'); + }); + + it('should return empty consent string when undefined', function () { + const requests = spec.buildRequests([bid]); + const gdpr = requests[0].url.search('gdpr_applies'); + const euconsent = requests[0].url.search('euconsent='); + expect(gdpr).to.equal(-1); + expect(euconsent).to.equal(-1); + }); + + it('should parse bids to request from pageContext', function () { + const bidCopy = { ...bid }; + bidCopy.params = { + ...bid.params, + pageContext: { + dv: 'test/areatest', + du: 'https://example.com/', + dr: 'https://example.org/', + keyWords: ['val1', 'val2'], + keyValues: { + adunit: 'test/areatest' + } + } + }; + const requests = spec.buildRequests([bidCopy, bid2]); + expect(requests[0].url).to.have.string(CSR_ENDPOINT); + expect(requests[0].url).to.have.string('slot0=test'); + expect(requests[0].url).to.have.string('id0=1'); + expect(requests[0].url).to.have.string('iusizes0=300x250%2C300x600'); + expect(requests[0].url).to.have.string('slot1=test2'); + expect(requests[0].url).to.have.string('id1=2'); + expect(requests[0].url).to.have.string('iusizes1=750x300'); + expect(requests[0].url).to.have.string('site=test'); + expect(requests[0].url).to.have.string('area=areatest'); + expect(requests[0].url).to.have.string('cre_format=html'); + expect(requests[0].url).to.have.string('systems=das'); + expect(requests[0].url).to.have.string('ems_url=1'); + expect(requests[0].url).to.have.string('bid_rate=1'); + expect(requests[0].url).to.have.string('du=https%3A%2F%2Fexample.com%2F'); + expect(requests[0].url).to.have.string('dr=https%3A%2F%2Fexample.org%2F'); + expect(requests[0].url).to.have.string('DV=test%2Fareatest'); + expect(requests[0].url).to.have.string('kwrd=val1%2Bval2'); + expect(requests[0].url).to.have.string('kvadunit=test%2Fareatest'); + }); + }); + + describe('interpretResponse', function () { + const response = { + 'adsCheck': 'ok', + 'geoloc': {}, + 'ir': '92effd60-0c84-4dac-817e-763ea7b8ac65', + 'ads': [ + { + 'id': 'flat-belkagorna', + 'slot': 'flat-belkagorna', + 'prio': 10, + 'type': 'html', + 'bid_rate': 0.321123, + 'adid': 'das,50463,152276', + 'id_3': '12734', + 'html': '' + } + ], + 'iv': '202003191334467636346500' + }; + + it('should get correct bid response', function () { + const resp = spec.interpretResponse({ body: response }, { bidIds: [{ slot: 'flat-belkagorna', bidId: 1 }] }); + expect(resp[0]).to.have.all.keys('cpm', 'currency', 'netRevenue', 'requestId', 'ttl', 'width', 'height', 'creativeId', 'dealId', 'ad', 'meta'); + expect(resp.length).to.equal(1); + }); + + it('should handle empty ad', function () { + let res = { + 'ads': [{ + type: 'empty' + }] + }; + const resp = spec.interpretResponse({ body: res }, {}); + expect(resp).to.deep.equal([]); + }); + + it('should handle empty server response', function () { + let res = { + 'ads': [] + }; + const resp = spec.interpretResponse({ body: res }, {}); + expect(resp).to.deep.equal([]); + }); + }); +}); From 6d8303b4477979cf207c2383c06fcf45332c4bee Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Thu, 14 Jul 2022 17:07:36 +0300 Subject: [PATCH 023/569] TheMediaGrid: added ortb2.user.data to gridNMBidAdapter (#8652) --- modules/gridNMBidAdapter.js | 49 ++++++++++++++++++++-- test/spec/modules/gridNMBidAdapter_spec.js | 32 ++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 63c42f60933..b44f94f0495 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -1,4 +1,13 @@ -import { isStr, deepAccess, isArray, isNumber, logError, logWarn, parseGPTSingleSizeArrayToRtbSize } from '../src/utils.js'; +import { + isStr, + deepAccess, + isArray, + isNumber, + logError, + logWarn, + parseGPTSingleSizeArrayToRtbSize, + mergeDeep +} from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO } from '../src/mediaTypes.js'; @@ -153,9 +162,7 @@ export const spec = { user = { data: [{ name: 'iow_labs_pub_data', - segment: jwpseg.map((seg) => { - return {name: 'jwpseg', value: seg}; - }) + segment: segmentProcessing(jwpseg, 'jwpseg'), }] }; } @@ -174,9 +181,27 @@ export const spec = { user.ext = userExt; } + const ortb2UserData = deepAccess(bidderRequest, 'ortb2.user.data'); + if (ortb2UserData && ortb2UserData.length) { + if (!user) { + user = { data: [] }; + } + user = mergeDeep(user, { + data: [...ortb2UserData] + }); + } + if (user) { request.user = user; } + const site = deepAccess(bidderRequest, 'ortb2.site'); + if (site) { + const data = deepAccess(site, 'content.data'); + if (data && data.length) { + const siteContent = request.site.content || {}; + request.site.content = mergeDeep(siteContent, { data }); + } + } if (gdprConsent && gdprConsent.gdprApplies) { request.regs = { @@ -408,4 +433,20 @@ export function getSyncUrl() { return SYNC_URL; } +function segmentProcessing(segment, forceSegName) { + return segment + .map((seg) => { + const value = seg && (seg.value || seg.id || seg); + if (typeof value === 'string' || typeof value === 'number') { + return { + value: value.toString(), + ...(forceSegName && { name: forceSegName }), + ...(seg.name && { name: seg.name }), + }; + } + return null; + }) + .filter((seg) => !!seg); +} + registerBidder(spec); diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index c09aca07cc3..b400ef3394d 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -226,6 +226,38 @@ describe('TheMediaGridNM Adapter', function () { } ]; + it('if content and segment is present in jwTargeting, payload must have right params', function () { + const jsContent = {id: 'test_jw_content_id'}; + const jsSegments = ['test_seg_1', 'test_seg_2']; + const bidRequestsWithJwTargeting = bidRequests.map((bid) => { + return Object.assign({ + rtd: { + jwplayer: { + targeting: { + segments: jsSegments, + content: jsContent + } + } + } + }, bid); + }); + const requests = spec.buildRequests(bidRequestsWithJwTargeting, bidderRequest); + requests.forEach((req, i) => { + const payload = req.data; + expect(req).to.have.property('data'); + expect(payload).to.have.property('user'); + expect(payload.user.data).to.deep.equal([{ + name: 'iow_labs_pub_data', + segment: [ + {name: 'jwpseg', value: jsSegments[0]}, + {name: 'jwpseg', value: jsSegments[1]} + ] + }]); + expect(payload).to.have.property('site'); + expect(payload.site.content).to.deep.equal(jsContent); + }); + }); + it('should attach valid params to the tag', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); const requestsSizes = ['300x250,300x600', '728x90']; From 995a010f409e613093a0561b27068f01efd58016 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 14 Jul 2022 14:57:51 +0000 Subject: [PATCH 024/569] Prebid 7.6.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index dfb3c6d93ae..e361117106c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.6.0-pre", + "version": "7.6.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 1a8e1ff1a98..67686017111 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.6.0-pre", + "version": "7.6.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a7d93318c6e1e2a58d7b33312258d8f98ecb9086 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 14 Jul 2022 14:57:51 +0000 Subject: [PATCH 025/569] Increment version to 7.7.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e361117106c..6b0a939a173 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.6.0", + "version": "7.7.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 67686017111..33dca05273f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.6.0", + "version": "7.7.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From b613039e1062d807d11f1d5cea85e8fd6f53a4a1 Mon Sep 17 00:00:00 2001 From: Anass Seddiki Date: Thu, 14 Jul 2022 19:05:50 +0200 Subject: [PATCH 026/569] 1plusX RTD submodule: New RTD Module (#8614) * Empty shell for 1plusX RTD submodule (#1) * Submodule initialization & functions (init; getBidRequestData) skeletons (#2) * Testing for init function (#3) * Requesting Profile API for Data (#4) * Extract PAPI response & implementation example * Transmitting targeting data to bidder adapters * Markdown file documentation * Code cleaned & jsDoc completed * Change contact email + beautify parameters table + fix type in param name * Change customerId param type to string in doc * Add 1plusXRtdProvider as submodule of rtdModule * Add more tests on extractConfig amongst others * Remove SUPPORTED_BIDDERS limitation * Remove supported bidders from docs * Write to site.content.data.segment.id & keep legacy support for appnexus * Change location of googleTagServices * Add segtax for site.content.data * Handle audiences for appNexus by putting them in config.appnexusAuctionKeywords --- .../gpt/1plusXRtdProviderExample.html | 112 +++++ modules/.submodules.json | 3 +- modules/1plusXRtdProvider.js | 251 ++++++++++ modules/1plusXRtdProvider.md | 67 +++ test/spec/modules/1plusXRtdProvider_spec.js | 430 ++++++++++++++++++ 5 files changed, 862 insertions(+), 1 deletion(-) create mode 100644 integrationExamples/gpt/1plusXRtdProviderExample.html create mode 100644 modules/1plusXRtdProvider.js create mode 100644 modules/1plusXRtdProvider.md create mode 100644 test/spec/modules/1plusXRtdProvider_spec.js diff --git a/integrationExamples/gpt/1plusXRtdProviderExample.html b/integrationExamples/gpt/1plusXRtdProviderExample.html new file mode 100644 index 00000000000..2eb75063df1 --- /dev/null +++ b/integrationExamples/gpt/1plusXRtdProviderExample.html @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + +

1plusX RTD Module for Prebid

+ +
+ +
+ + + \ No newline at end of file diff --git a/modules/.submodules.json b/modules/.submodules.json index d808b10051b..d24e7ff96f5 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -49,6 +49,7 @@ "dfpAdServerVideo" ], "rtdModule": [ + "1plusXRtdProvider", "airgridRtdProvider", "akamaiDapRtdProvider", "blueconicRtdProvider", @@ -84,4 +85,4 @@ ] } } -} +} \ No newline at end of file diff --git a/modules/1plusXRtdProvider.js b/modules/1plusXRtdProvider.js new file mode 100644 index 00000000000..5affafcf9d3 --- /dev/null +++ b/modules/1plusXRtdProvider.js @@ -0,0 +1,251 @@ +import { submodule } from '../src/hook.js'; +import { config } from '../src/config.js'; +import { ajax } from '../src/ajax.js'; +import { + logMessage, logError, + deepAccess, mergeDeep, + isNumber, isArray, deepSetValue +} from '../src/utils.js'; + +// Constants +const REAL_TIME_MODULE = 'realTimeData'; +const MODULE_NAME = '1plusX'; +const ORTB2_NAME = '1plusX.com' +const PAPI_VERSION = 'v1.0'; +const LOG_PREFIX = '[1plusX RTD Module]: '; +const LEGACY_SITE_KEYWORDS_BIDDERS = ['appnexus']; +export const segtaxes = { + // cf. https://github.com/InteractiveAdvertisingBureau/openrtb/pull/108 + AUDIENCE: 526, + CONTENT: 527, +}; +// Functions +/** + * Extracts the parameters for 1plusX RTD module from the config object passed at instanciation + * @param {Object} moduleConfig Config object passed to the module + * @param {Object} reqBidsConfigObj Config object for the bidders; each adapter has its own entry + * @returns {Object} Extracted configuration parameters for the module + */ +export const extractConfig = (moduleConfig, reqBidsConfigObj) => { + // CustomerId + const customerId = deepAccess(moduleConfig, 'params.customerId'); + if (!customerId) { + throw new Error('Missing parameter customerId in moduleConfig'); + } + // Timeout + const tempTimeout = deepAccess(moduleConfig, 'params.timeout'); + const timeout = isNumber(tempTimeout) && tempTimeout > 300 ? tempTimeout : 1000; + + // Bidders + const biddersTemp = deepAccess(moduleConfig, 'params.bidders'); + if (!isArray(biddersTemp) || !biddersTemp.length) { + throw new Error('Missing parameter bidders in moduleConfig'); + } + + const adUnitBidders = reqBidsConfigObj.adUnits + .flatMap(({ bids }) => bids.map(({ bidder }) => bidder)) + .filter((e, i, a) => a.indexOf(e) === i); + if (!isArray(adUnitBidders) || !adUnitBidders.length) { + throw new Error('Missing parameter bidders in bidRequestConfig'); + } + + const bidders = biddersTemp.filter(bidder => adUnitBidders.includes(bidder)); + if (!bidders.length) { + throw new Error('No bidRequestConfig bidder found in moduleConfig bidders'); + } + + return { customerId, timeout, bidders }; +} + +/** + * Gets the URL of Profile Api from which targeting data will be fetched + * @param {Object} config + * @param {string} config.customerId + * @returns {string} URL to access 1plusX Profile API + */ +const getPapiUrl = ({ customerId }) => { + // https://[yourClientId].profiles.tagger.opecloud.com/[VERSION]/targeting?url= + const currentUrl = encodeURIComponent(window.location.href); + const papiUrl = `https://${customerId}.profiles.tagger.opecloud.com/${PAPI_VERSION}/targeting?url=${currentUrl}`; + return papiUrl; +} + +/** + * Fetches targeting data. It contains the audience segments & the contextual topics + * @param {string} papiUrl URL of profile API + * @returns {Promise} Promise object resolving with data fetched from Profile API + */ +const getTargetingDataFromPapi = (papiUrl) => { + return new Promise((resolve, reject) => { + const requestOptions = { + customHeaders: { + 'Accept': 'application/json' + } + } + const callbacks = { + success(responseText, response) { + resolve(JSON.parse(response.response)); + }, + error(error) { + reject(error); + } + }; + ajax(papiUrl, callbacks, null, requestOptions) + }) +} + +/** + * Prepares the update for the ORTB2 object + * @param {Object} targetingData Targeting data fetched from Profile API + * @param {string[]} segments Represents the audience segments of the user + * @param {string[]} topics Represents the topics of the page + * @returns {Object} Object describing the updates to make on bidder configs + */ +export const buildOrtb2Updates = ({ segments = [], topics = [] }, bidder) => { + // Currently appnexus bidAdapter doesn't support topics in `site.content.data.segment` + // Therefore, writing them in `site.keywords` until it's supported + // Other bidAdapters do fine with `site.content.data.segment` + const writeToLegacySiteKeywords = LEGACY_SITE_KEYWORDS_BIDDERS.includes(bidder); + if (writeToLegacySiteKeywords) { + const site = { + keywords: topics.join(',') + }; + return { site }; + } + + const userData = { + name: ORTB2_NAME, + segment: segments.map((segmentId) => ({ id: segmentId })) + }; + const siteContentData = { + name: ORTB2_NAME, + segment: topics.map((topicId) => ({ id: topicId })), + ext: { segtax: segtaxes.CONTENT } + } + return { userData, siteContentData }; +} + +/** + * Merges the targeting data with the existing config for bidder and updates + * @param {string} bidder Bidder for which to set config + * @param {Object} ortb2Updates Updates to be applied to bidder config + * @param {Object} bidderConfigs All current bidder configs + * @returns {Object} Updated bidder config + */ +export const updateBidderConfig = (bidder, ortb2Updates, bidderConfigs) => { + const { site, siteContentData, userData } = ortb2Updates; + const bidderConfigCopy = mergeDeep({}, bidderConfigs[bidder]); + + if (site) { + // Legacy : cf. comment on buildOrtb2Updates first lines + const currentSite = deepAccess(bidderConfigCopy, 'ortb2.site') + const updatedSite = mergeDeep(currentSite, site); + deepSetValue(bidderConfigCopy, 'ortb2.site', updatedSite); + } + + if (siteContentData) { + const siteDataPath = 'ortb2.site.content.data'; + const currentSiteContentData = deepAccess(bidderConfigCopy, siteDataPath) || []; + const updatedSiteContentData = [ + ...currentSiteContentData.filter(({ name }) => name != siteContentData.name), + siteContentData + ]; + deepSetValue(bidderConfigCopy, siteDataPath, updatedSiteContentData); + } + + if (userData) { + const userDataPath = 'ortb2.user.data'; + const currentUserData = deepAccess(bidderConfigCopy, userDataPath) || []; + const updatedUserData = [ + ...currentUserData.filter(({ name }) => name != userData.name), + userData + ]; + deepSetValue(bidderConfigCopy, userDataPath, updatedUserData); + } + + return bidderConfigCopy; +}; + +const setAppnexusAudiences = (audiences) => { + config.setConfig({ + appnexusAuctionKeywords: { + '1plusX': audiences, + }, + }); +} + +/** + * Updates bidder configs with the targeting data retreived from Profile API + * @param {Object} papiResponse Response from Profile API + * @param {Object} config Module configuration + * @param {string[]} config.bidders Bidders specified in module's configuration + */ +export const setTargetingDataToConfig = (papiResponse, { bidders }) => { + const bidderConfigs = config.getBidderConfig(); + const { s: segments, t: topics } = papiResponse; + + for (const bidder of bidders) { + const ortb2Updates = buildOrtb2Updates({ segments, topics }, bidder); + const updatedBidderConfig = updateBidderConfig(bidder, ortb2Updates, bidderConfigs); + if (updatedBidderConfig) { + config.setBidderConfig({ + bidders: [bidder], + config: updatedBidderConfig + }); + } + if (bidder === 'appnexus') { + // Do the legacy stuff for appnexus with segments + setAppnexusAudiences(segments); + } + } +} + +// Functions exported in submodule object +/** + * Init + * @param {Object} config Module configuration + * @param {boolean} userConsent + * @returns true + */ +const init = (config, userConsent) => { + return true; +} + +/** + * + * @param {Object} reqBidsConfigObj Bid request configuration object + * @param {Function} callback Called on completion + * @param {Object} moduleConfig Configuration for 1plusX RTD module + * @param {boolean} userConsent + */ +const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, userConsent) => { + try { + // Get the required config + const { customerId, bidders } = extractConfig(moduleConfig, reqBidsConfigObj); + // Get PAPI URL + const papiUrl = getPapiUrl({ customerId }) + // Call PAPI + getTargetingDataFromPapi(papiUrl) + .then((papiResponse) => { + logMessage(LOG_PREFIX, 'Get targeting data request successful'); + setTargetingDataToConfig(papiResponse, { bidders }); + callback(); + }) + .catch((error) => { + throw error; + }) + } catch (error) { + logError(LOG_PREFIX, error); + callback(); + } +} + +// The RTD submodule object to be exported +export const onePlusXSubmodule = { + name: MODULE_NAME, + init, + getBidRequestData +} + +// Register the onePlusXSubmodule as submodule of realTimeData +submodule(REAL_TIME_MODULE, onePlusXSubmodule); diff --git a/modules/1plusXRtdProvider.md b/modules/1plusXRtdProvider.md new file mode 100644 index 00000000000..75ad3b966d1 --- /dev/null +++ b/modules/1plusXRtdProvider.md @@ -0,0 +1,67 @@ +# 1plusX Real-time Data Submodule + +## Overview + + Module Name: 1plusX Rtd Provider + Module Type: Rtd Provider + Maintainer: dc-team-1px@triplelift.com + +## Description + +RTD provider for 1plusX. +Enriches the bidding object with Audience & Targeting data +Contact dc-team-1px@triplelift.com for information. + +## Usage + +### Build +``` +gulp build --modules="rtdModule,1plusXRtdProvider,appnexusBidAdapter,..." +``` + +> Note that the global RTD module, `rtdModule`, is a prerequisite of the 1plusX RTD module. + +### Configuration + +Use `setConfig` to instruct Prebid.js to initilize the 1plusX RTD module, as specified below. + +This module is configured as part of the `realTimeData.dataProviders` + +```javascript +var TIMEOUT = 1000; +pbjs.setConfig({ + realTimeData: { + auctionDelay: TIMEOUT, + dataProviders: [{ + name: '1plusX', + waitForIt: true, + params: { + customerId: 'acme', + bidders: ['appnexus', 'rubicon'], + timeout: TIMEOUT + } + }] + } +}); +``` + +### Parameters + +| Name | Type | Description | Notes | +| :---------------- | :------------ | :--------------------------------------------------------------- |:-------------------------------------------------------- | +| name | String | Real time data module name | Always '1plusX' | +| waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (optional) | `false` | +| params | Object | | | +| params.customerId | String | Your 1plusX customer id | | +| params.bidders | Array | List of bidders for which you would like data to be set | | +| params.timeout | Integer | timeout (ms) | 1000ms | + +## Testing + +To view an example of how the 1plusX RTD module works : + +`gulp serve --modules=rtdModule,1plusXRtdProvider,appnexusBidAdapter,rubiconBidAdapter` + +and then point your browser at: + +`http://localhost:9999/integrationExamples/gpt/1plusXRtdProvider_example.html` diff --git a/test/spec/modules/1plusXRtdProvider_spec.js b/test/spec/modules/1plusXRtdProvider_spec.js new file mode 100644 index 00000000000..9682e4b62f8 --- /dev/null +++ b/test/spec/modules/1plusXRtdProvider_spec.js @@ -0,0 +1,430 @@ +import { config } from 'src/config'; +import { + onePlusXSubmodule, + segtaxes, + extractConfig, + buildOrtb2Updates, + updateBidderConfig, + setTargetingDataToConfig +} from 'modules/1plusXRtdProvider'; + +describe('1plusXRtdProvider', () => { + // Fake server config + let fakeServer; + const fakeResponseHeaders = { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }; + const fakeResponse = { + s: ['segment1', 'segment2', 'segment3'], + t: ['targeting1', 'targeting2', 'targeting3'] + }; + + // Bid request config + const reqBidsConfigObj = { + adUnits: [{ + bids: [ + { bidder: 'appnexus' } + ] + }] + }; + + // Bidder configs + const bidderConfigInitial = { + ortb2: { + user: { keywords: '' }, + site: { ext: {} } + } + } + const bidderConfigInitialWith1plusXUserData = { + ortb2: { + user: { + data: [{ name: '1plusX.com', segment: [{ id: 'initial' }] }] + }, + site: { content: { data: [] } } + } + } + const bidderConfigInitialWithUserData = { + ortb2: { + user: { + data: [{ name: 'hello.world', segment: [{ id: 'initial' }] }] + }, + site: { content: { data: [] } } + } + } + const bidderConfigInitialWith1plusXSiteContent = { + ortb2: { + user: { data: [] }, + site: { + content: { + data: [{ + name: '1plusX.com', segment: [{ id: 'initial' }], ext: { segtax: 525 } + }] + } + }, + } + } + const bidderConfigInitialWithSiteContent = { + ortb2: { + user: { data: [] }, + site: { + content: { + data: [{ name: 'hello.world', segment: [{ id: 'initial' }] }] + } + }, + } + } + // Util functions + const randomBidder = (len = 5) => Math.random().toString(36).replace(/[^a-z]+/g, '').substring(0, len); + + before(() => { + config.resetConfig(); + }) + + after(() => { }) + + beforeEach(() => { + fakeServer = sinon.createFakeServer(); + fakeServer.respondWith('GET', '*', [200, fakeResponseHeaders, JSON.stringify(fakeResponse)]); + fakeServer.respondImmediately = true; + fakeServer.autoRespond = true; + }) + + describe('onePlusXSubmodule', () => { + it('init is successfull', () => { + const initResult = onePlusXSubmodule.init(); + expect(initResult).to.be.true; + }) + + it('callback is called after getBidRequestData', () => { + // Nice case; everything runs as expected + { + const callbackSpy = sinon.spy(); + const config = { params: { customerId: 'test', bidders: ['appnexus'] } }; + onePlusXSubmodule.getBidRequestData(reqBidsConfigObj, callbackSpy, config); + setTimeout(() => { + expect(callbackSpy.calledOnce).to.be.true + }, 100) + } + // No customer id in config => error but still callback called + { + const callbackSpy = sinon.spy(); + const config = {} + onePlusXSubmodule.getBidRequestData(reqBidsConfigObj, callbackSpy, config); + setTimeout(() => { + expect(callbackSpy.calledOnce).to.be.true + }, 100); + } + // No bidders in config => error but still callback called + { + const callbackSpy = sinon.spy(); + const config = { customerId: 'test' } + onePlusXSubmodule.getBidRequestData(reqBidsConfigObj, callbackSpy, config); + setTimeout(() => { + expect(callbackSpy.calledOnce).to.be.true + }, 100); + } + }) + }) + + describe('extractConfig', () => { + const customerId = 'test'; + const timeout = 1000; + const bidders = ['appnexus']; + + it('Throws an error if no customerId is specified', () => { + const moduleConfig = { params: { timeout, bidders } }; + expect(() => extractConfig(moduleConfig, reqBidsConfigObj)).to.throw(); + }) + it('Throws an error if no bidder is specified', () => { + const moduleConfig = { params: { customerId, timeout } }; + expect(() => extractConfig(moduleConfig, reqBidsConfigObj)).to.throw(); + }) + it("Throws an error if there's no bidder in reqBidsConfigObj", () => { + const moduleConfig = { params: { customerId, timeout, bidders } }; + const reqBidsConfigEmpty = { adUnits: [{ bids: [] }] }; + expect(() => extractConfig(moduleConfig, reqBidsConfigEmpty)).to.throw(); + }) + it('Returns an object containing the parameters specified', () => { + const moduleConfig = { params: { customerId, timeout, bidders } }; + const expectedKeys = ['customerId', 'timeout', 'bidders'] + const extractedConfig = extractConfig(moduleConfig, reqBidsConfigObj); + expect(extractedConfig).to.be.an('object').and.to.have.all.keys(expectedKeys); + expect(extractedConfig.customerId).to.equal(customerId); + expect(extractedConfig.timeout).to.equal(timeout); + expect(extractedConfig.bidders).to.deep.equal(bidders); + }) + /* 1plusX RTD module may only use bidders that are both specified in : + - the bid request configuration + - AND in the 1plusX RTD module configuration + Below 2 tests are enforcing those rules + */ + it('Returns the intersection of bidders found in bid request config & module config', () => { + const bidders = ['appnexus', 'rubicon']; + const moduleConfig = { params: { customerId, timeout, bidders } }; + const { bidders: extractedBidders } = extractConfig(moduleConfig, reqBidsConfigObj); + expect(extractedBidders).to.be.an('array').and.to.have.length(1); 7 + expect(extractedBidders[0]).to.equal('appnexus'); + }) + it('Throws an error if no bidder can be used by the module', () => { + const bidders = ['rubicon']; + const moduleConfig = { params: { customerId, timeout, bidders } }; + expect(() => extractConfig(moduleConfig, reqBidsConfigObj)).to.throw(); + }) + }) + + describe('buildOrtb2Updates', () => { + it('fills site.content.data & user.data in the ortb2 config', () => { + const rtdData = { segments: fakeResponse.s, topics: fakeResponse.t }; + const ortb2Updates = buildOrtb2Updates(rtdData, randomBidder()); + + const expectedOutput = { + siteContentData: { + name: '1plusX.com', + segment: rtdData.topics.map((topicId) => ({ id: topicId })), + ext: { segtax: segtaxes.CONTENT } + }, + userData: { + name: '1plusX.com', + segment: rtdData.segments.map((segmentId) => ({ id: segmentId })) + } + } + expect([ortb2Updates]).to.deep.include.members([expectedOutput]); + }); + it('fills site.keywords in the ortb2 config (appnexus specific)', () => { + const rtdData = { segments: fakeResponse.s, topics: fakeResponse.t }; + const ortb2Updates = buildOrtb2Updates(rtdData, 'appnexus'); + + const expectedOutput = { + site: { + keywords: rtdData.topics.join(','), + } + } + expect([ortb2Updates]).to.deep.include.members([expectedOutput]); + }); + + it('defaults to empty array if no segment is given', () => { + const rtdData = { topics: fakeResponse.t }; + const ortb2Updates = buildOrtb2Updates(rtdData, randomBidder()); + + const expectedOutput = { + siteContentData: { + name: '1plusX.com', + segment: rtdData.topics.map((topicId) => ({ id: topicId })), + ext: { segtax: segtaxes.CONTENT } + }, + userData: { + name: '1plusX.com', + segment: [] + } + } + expect(ortb2Updates).to.deep.include(expectedOutput); + }) + + it('defaults to empty array if no topic is given', () => { + const rtdData = { segments: fakeResponse.s }; + const ortb2Updates = buildOrtb2Updates(rtdData, randomBidder()); + + const expectedOutput = { + siteContentData: { + name: '1plusX.com', + segment: [], + ext: { segtax: segtaxes.CONTENT } + }, + userData: { + name: '1plusX.com', + segment: rtdData.segments.map((segmentId) => ({ id: segmentId })) + } + } + expect(ortb2Updates).to.deep.include(expectedOutput); + }) + it('defaults to empty string if no topic is given (appnexus specific)', () => { + const rtdData = { segments: fakeResponse.s }; + const ortb2Updates = buildOrtb2Updates(rtdData, 'appnexus'); + + const expectedOutput = { + site: { + keywords: '', + } + } + expect(ortb2Updates).to.deep.include(expectedOutput); + }) + }) + + describe('updateBidderConfig', () => { + const ortb2UpdatesAppNexus = { + site: { + keywords: fakeResponse.t.join(','), + }, + userData: { + name: '1plusX.com', + segment: fakeResponse.s.map((segmentId) => ({ id: segmentId })) + } + } + const ortb2Updates = { + siteContentData: { + name: '1plusX.com', + segment: fakeResponse.t.map((topicId) => ({ id: topicId })), + ext: { segtax: segtaxes.CONTENT } + }, + userData: { + name: '1plusX.com', + segment: fakeResponse.s.map((segmentId) => ({ id: segmentId })) + } + } + + it('merges fetched data in bidderConfig for configured bidders', () => { + const bidder = randomBidder(); + // Set initial config + config.setBidderConfig({ + bidders: [bidder], + config: bidderConfigInitial + }); + // Call submodule's setBidderConfig + const newBidderConfig = updateBidderConfig(bidder, ortb2Updates, config.getBidderConfig()); + // Check that the targeting data has been set in the config + expect(newBidderConfig).not.to.be.null; + expect(newBidderConfig.ortb2.site.content.data).to.deep.include(ortb2Updates.siteContentData); + expect(newBidderConfig.ortb2.user.data).to.deep.include(ortb2Updates.userData); + // Check that existing config didn't get erased + expect(newBidderConfig.ortb2.site).to.deep.include(bidderConfigInitial.ortb2.site); + expect(newBidderConfig.ortb2.user).to.deep.include(bidderConfigInitial.ortb2.user); + }) + + it('merges fetched data in bidderConfig for configured bidders (appnexus specific)', () => { + const bidder = 'appnexus'; + // Set initial config + config.setBidderConfig({ + bidders: [bidder], + config: bidderConfigInitial + }); + // Call submodule's setBidderConfig + const newBidderConfig = updateBidderConfig(bidder, ortb2UpdatesAppNexus, config.getBidderConfig()); + + // Check that the targeting data has been set in the config + expect(newBidderConfig).not.to.be.null; + expect(newBidderConfig.ortb2.site).to.deep.include(ortb2UpdatesAppNexus.site); + // Check that existing config didn't get erased + expect(newBidderConfig.ortb2.site).to.deep.include(bidderConfigInitial.ortb2.site); + expect(newBidderConfig.ortb2.user).to.deep.include(bidderConfigInitial.ortb2.user); + }) + + it('overwrites an existing 1plus.com entry in ortb2.user.data', () => { + const bidder = randomBidder(); + // Set initial config + config.setBidderConfig({ + bidders: [bidder], + config: bidderConfigInitialWith1plusXUserData + }); + // Save previous user.data entry + const previousUserData = bidderConfigInitialWith1plusXUserData.ortb2.user.data[0]; + // Call submodule's setBidderConfig + const newBidderConfig = updateBidderConfig(bidder, ortb2Updates, config.getBidderConfig()); + // Check that the targeting data has been set in the config + expect(newBidderConfig).not.to.be.null; + expect(newBidderConfig.ortb2.user.data).to.deep.include(ortb2Updates.userData); + expect(newBidderConfig.ortb2.user.data).not.to.include(previousUserData); + }) + it("doesn't overwrite entries in ortb2.user.data that aren't 1plusx.com", () => { + const bidder = randomBidder(); + // Set initial config + config.setBidderConfig({ + bidders: [bidder], + config: bidderConfigInitialWithUserData + }); + // Save previous user.data entry + const previousUserData = bidderConfigInitialWithUserData.ortb2.user.data[0]; + // Call submodule's setBidderConfig + const newBidderConfig = updateBidderConfig(bidder, ortb2Updates, config.getBidderConfig()); + // Check that the targeting data has been set in the config + expect(newBidderConfig).not.to.be.null; + expect(newBidderConfig.ortb2.user.data).to.deep.include(ortb2Updates.userData); + expect(newBidderConfig.ortb2.user.data).to.deep.include(previousUserData); + }) + + it('overwrites an existing 1plus.com entry in ortb2.site.content.data', () => { + const bidder = randomBidder(); + // Set initial config + config.setBidderConfig({ + bidders: [bidder], + config: bidderConfigInitialWith1plusXSiteContent + }); + // Save previous user.data entry + const previousSiteContent = bidderConfigInitialWith1plusXSiteContent.ortb2.site.content.data[0]; + // Call submodule's setBidderConfig + const newBidderConfig = updateBidderConfig(bidder, ortb2Updates, config.getBidderConfig()); + // Check that the targeting data has been set in the config + expect(newBidderConfig).not.to.be.null; + expect(newBidderConfig.ortb2.site.content.data).to.deep.include(ortb2Updates.siteContentData); + expect(newBidderConfig.ortb2.site.content.data).not.to.include(previousSiteContent); + }) + it("doesn't overwrite entries in ortb2.site.content.data that aren't 1plusx.com", () => { + const bidder = randomBidder(); + // Set initial config + config.setBidderConfig({ + bidders: [bidder], + config: bidderConfigInitialWithSiteContent + }); + // Save previous user.data entry + const previousSiteContent = bidderConfigInitialWithSiteContent.ortb2.site.content.data[0]; + // Call submodule's setBidderConfig + const newBidderConfig = updateBidderConfig(bidder, ortb2Updates, config.getBidderConfig()); + // Check that the targeting data has been set in the config + expect(newBidderConfig).not.to.be.null; + expect(newBidderConfig.ortb2.site.content.data).to.deep.include(ortb2Updates.siteContentData); + expect(newBidderConfig.ortb2.site.content.data).to.deep.include(previousSiteContent); + }) + }) + + describe('setTargetingDataToConfig', () => { + const expectedKeywords = fakeResponse.t.join(','); + const expectedSiteContentObj = { + data: [{ + name: '1plusX.com', + segment: fakeResponse.t.map((topicId) => ({ id: topicId })), + ext: { segtax: segtaxes.CONTENT } + }] + } + const expectedUserObj = { + data: [{ + name: '1plusX.com', + segment: fakeResponse.s.map((segmentId) => ({ id: segmentId })) + }] + } + const expectedOrtb2 = { + appnexus: { + site: { keywords: expectedKeywords } + }, + rubicon: { + site: { content: expectedSiteContentObj }, + user: expectedUserObj + } + } + + it('sets the config for the selected bidders', () => { + const bidders = ['appnexus', 'rubicon']; + // setting initial config for those bidders + config.setBidderConfig({ + bidders, + config: bidderConfigInitial + }) + // call setTargetingDataToConfig + setTargetingDataToConfig(fakeResponse, { bidders }); + + // Check that the targeting data has been set in both configs + for (const bidder of bidders) { + const newConfig = config.getBidderConfig()[bidder]; + // Check that we got what we expect + const expectedConfErr = (prop) => `New config for ${bidder} doesn't comply with expected at ${prop}`; + expect(newConfig.ortb2.site, expectedConfErr('site')).to.deep.include(expectedOrtb2[bidder].site); + if (expectedOrtb2[bidder].user) { + expect(newConfig.ortb2.user, expectedConfErr('user')).to.deep.include(expectedOrtb2[bidder].user); + } + // Check that existing config didn't get erased + const existingConfErr = (prop) => `Existing config for ${bidder} got unlawfully overwritten at ${prop}`; + expect(newConfig.ortb2.site, existingConfErr('site')).to.deep.include(bidderConfigInitial.ortb2.site); + expect(newConfig.ortb2.user, existingConfErr('user')).to.deep.include(bidderConfigInitial.ortb2.user); + } + }) + }) +}) From 4e1ed0c527c0e9dfcf1ef2642349df7b9454c16e Mon Sep 17 00:00:00 2001 From: shahinrahbariasl <56240400+shahinrahbariasl@users.noreply.github.com> Date: Thu, 14 Jul 2022 13:07:34 -0400 Subject: [PATCH 027/569] IX Bid Adapter: add inventory identifiers info to ixDiag (#8683) Co-authored-by: shahin.rahbariasl --- modules/ixBidAdapter.js | 43 +++++++++++++++++++++++++- test/spec/modules/ixBidAdapter_spec.js | 15 +++++++-- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index c22c0af7f4d..136cf6f25f0 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -244,6 +244,8 @@ function bidToVideoImp(bid) { } imp.video = videoParamRef ? deepClone(bid.params.video) : {}; + // populate imp level transactionId + imp.ext.tid = deepAccess(bid, 'ortb2Imp.ext.tid'); // copy all video properties to imp object for (const adUnitProperty in videoAdUnitRef) { @@ -355,6 +357,9 @@ function bidToNativeImp(bid) { ver: '1.2' }; + // populate imp level transactionId + imp.ext.tid = deepAccess(bid, 'ortb2Imp.ext.tid'); + _applyFloor(bid, imp, NATIVE); return imp; @@ -1126,6 +1131,22 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } } + // add identifiers info to ixDiag + const pbaAdSlot = impressions[transactionIds[adUnitIndex]].pbadslot + const tagId = impressions[transactionIds[adUnitIndex]].tagId + const adUnitCode = impressions[transactionIds[adUnitIndex]].adUnitCode + const divId = impressions[transactionIds[adUnitIndex]].divId + if (pbaAdSlot || tagId || adUnitCode || divId) { + const clonedRObject = deepClone(r); + const requestSize = `${baseUrl}${parseQueryStringParameters({ ...payload, r: JSON.stringify(clonedRObject) })}`.length; + if (requestSize < MAX_REQUEST_SIZE) { + r.ext.ixdiag.pbadslot = pbaAdSlot; + r.ext.ixdiag.tagid = tagId; + r.ext.ixdiag.adunitcode = adUnitCode; + r.ext.ixdiag.divId = divId; + } + } + const isLastAdUnit = adUnitIndex === transactionIds.length - 1; if (wasAdUnitImpressionsTrimmed || isLastAdUnit) { @@ -1259,6 +1280,13 @@ function createNativeImps(validBidRequest, nativeImps) { nativeImps[validBidRequest.transactionId].ixImps = []; nativeImps[validBidRequest.transactionId].ixImps.push(imp); nativeImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid') + nativeImps[validBidRequest.transactionId].pbadslot = deepAccess(validBidRequest, 'ortb2Imp.ext.data.pbadslot'); + nativeImps[validBidRequest.transactionId].tagId = deepAccess(validBidRequest, 'params.tagId'); + + const adUnitCode = validBidRequest.adUnitCode; + const divId = document.getElementById(adUnitCode) ? adUnitCode : getGptSlotInfoForAdUnitCode(adUnitCode).divId; + nativeImps[validBidRequest.transactionId].adUnitCode = adUnitCode; + nativeImps[validBidRequest.transactionId].divId = divId; } } @@ -1274,7 +1302,13 @@ function createVideoImps(validBidRequest, videoImps) { videoImps[validBidRequest.transactionId].ixImps = []; videoImps[validBidRequest.transactionId].ixImps.push(imp); videoImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); - videoImps[validBidRequest.transactionId].tid = deepAccess(validBidRequest, 'ortb2Imp.ext.tid'); + videoImps[validBidRequest.transactionId].pbadslot = deepAccess(validBidRequest, 'ortb2Imp.ext.data.pbadslot'); + videoImps[validBidRequest.transactionId].tagId = deepAccess(validBidRequest, 'params.tagId'); + + const adUnitCode = validBidRequest.adUnitCode; + const divId = document.getElementById(adUnitCode) ? adUnitCode : getGptSlotInfoForAdUnitCode(adUnitCode).divId; + videoImps[validBidRequest.transactionId].adUnitCode = adUnitCode; + videoImps[validBidRequest.transactionId].divId = divId; } } @@ -1302,6 +1336,13 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { bannerImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); bannerImps[validBidRequest.transactionId].dfp_ad_unit_code = deepAccess(validBidRequest, 'ortb2Imp.ext.data.adserver.adslot'); bannerImps[validBidRequest.transactionId].tid = deepAccess(validBidRequest, 'ortb2Imp.ext.tid'); + bannerImps[validBidRequest.transactionId].pbadslot = deepAccess(validBidRequest, 'ortb2Imp.ext.data.pbadslot'); + bannerImps[validBidRequest.transactionId].tagId = deepAccess(validBidRequest, 'params.tagId'); + + const adUnitCode = validBidRequest.adUnitCode; + const divId = document.getElementById(adUnitCode) ? adUnitCode : getGptSlotInfoForAdUnitCode(adUnitCode).divId; + bannerImps[validBidRequest.transactionId].adUnitCode = adUnitCode; + bannerImps[validBidRequest.transactionId].divId = divId; // Create IX imps from params.size if (bannerSizeDefined) { diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 233fac6f426..a25e6b1c6a0 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -290,6 +290,7 @@ describe('IndexexchangeAdapter', function () { { bidder: 'ix', params: { + tagId: '123', siteId: '123', size: [300, 250], }, @@ -304,7 +305,10 @@ describe('IndexexchangeAdapter', function () { }, ortb2Imp: { ext: { - tid: '173f49a8-7549-4218-a23c-e7ba59b47230' + tid: '173f49a8-7549-4218-a23c-e7ba59b47230', + data: { + pbadslot: 'div-gpt-ad-1460505748562-0' + } } }, adUnitCode: 'div-gpt-ad-1460505748562-0', @@ -320,6 +324,7 @@ describe('IndexexchangeAdapter', function () { { bidder: 'ix', params: { + tagId: '123', siteId: '456', video: { skippable: false, @@ -344,7 +349,10 @@ describe('IndexexchangeAdapter', function () { }, ortb2Imp: { ext: { - tid: '173f49a8-7549-4218-a23c-e7ba59b47230' + tid: '173f49a8-7549-4218-a23c-e7ba59b47230', + data: { + pbadslot: 'div-gpt-ad-1460505748562-0' + } } }, adUnitCode: 'div-gpt-ad-1460505748562-0', @@ -2614,6 +2622,9 @@ describe('IndexexchangeAdapter', function () { expect(diagObj.allu).to.equal(2); expect(diagObj.version).to.equal('$prebid.version$'); expect(diagObj.url).to.equal('http://localhost:9876/context.html') + expect(diagObj.pbadslot).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].ortb2Imp.ext.data.pbadslot) + expect(diagObj.tagid).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.tagId) + expect(diagObj.adunitcode).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].adUnitCode) }); }); }); From d73091c53673c243f86006299c5f8035436f8d81 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 15 Jul 2022 19:55:08 +0200 Subject: [PATCH 028/569] Impactify Bid Adapter: Remove use of local storage debugging flag (#8693) * Remove use of local storage As requested, we remove the use of local storage. https://github.com/prebid/Prebid.js/issues/8689 * Update impactifyBidAdapter.js --- modules/impactifyBidAdapter.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index a4010772db0..bb3ad63085c 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -37,9 +37,13 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => { source: {tid: bidderRequest.auctionId} }; + // Get the url parameters + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const checkPrebid = urlParams.get('_checkPrebid'); // Force impactify debugging parameter - if (window.localStorage.getItem('_im_db_bidder') != null) { - request.test = Number(window.localStorage.getItem('_im_db_bidder')); + if (checkPrebid != null) { + request.test = Number(checkPrebid); } // Set Schain in request From cd91429609c28eff4dec0c4037aa2fb19bad281a Mon Sep 17 00:00:00 2001 From: sag-jonhil <78849369+sag-jonhil@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:29:55 +0200 Subject: [PATCH 029/569] Seeding Alliance Bid Adapter: Add GVLID (#8697) * add seedingAlliance Adapter * add two native default params * ... * ... * seedingAlliance Adapter: add two more default native params * updating seedingAlliance Adapter * seedingAlliance Adapter * quickfix no bids + net revenue * bugfix replace auction price * change URL and add versioning * add vendorId to seedingAllianceAdapter Co-authored-by: SeedingAllianceTech <55976067+SeedingAllianceTech@users.noreply.github.com> Co-authored-by: Hendrick Musche <107099114+sag-henmus@users.noreply.github.com> Co-authored-by: Hendrick Musche --- modules/seedingAllianceBidAdapter.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/seedingAllianceBidAdapter.js b/modules/seedingAllianceBidAdapter.js index 98b27de678c..1b178b59728 100755 --- a/modules/seedingAllianceBidAdapter.js +++ b/modules/seedingAllianceBidAdapter.js @@ -7,6 +7,7 @@ import { _map, deepSetValue, isEmpty, deepAccess } from '../src/utils.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'seedingAlliance'; +const GVL_ID = 371; const DEFAULT_CUR = 'EUR'; const ENDPOINT_URL = 'https://b.nativendo.de/cds/rtb/bid?format=openrtb2.5&ssp=pb'; @@ -52,6 +53,8 @@ const NATIVE_PARAMS = { export const spec = { code: BIDDER_CODE, + gvlid: GVL_ID, + supportedMediaTypes: [NATIVE], isBidRequestValid: function(bid) { From 00436fb5bd8e52a6969a3068b88cdeb0c17b7b3c Mon Sep 17 00:00:00 2001 From: jxdeveloper1 <71084096+jxdeveloper1@users.noreply.github.com> Date: Mon, 18 Jul 2022 20:40:37 +0800 Subject: [PATCH 030/569] Jixie Bid Adapter: change cookie ls names (#8695) * Adapter does not seem capable of supporting advertiserDomains #6650 added response comment and some trivial code. * removed a blank line at the end of file added a space behind the // in comments * in response to comment from reviewer. add the aspect of advertiserdomain in unit tests * added the code to get the keywords from the meta tags if available. * changed cookie ls names --- modules/jixieBidAdapter.js | 20 ++++++++++---------- test/spec/modules/jixieBidAdapter_spec.js | 16 ++++++++-------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index 2de2de6b481..f6f58883990 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -28,14 +28,14 @@ function setIds_(clientId, sessionId) { let expC = (new Date(new Date().setFullYear(new Date().getFullYear() + 1))).toUTCString(); let expS = (new Date(new Date().setMinutes(new Date().getMinutes() + sidTTLMins_))).toUTCString(); - storage.setCookie('_jx', clientId, expC, 'None', null); - storage.setCookie('_jx', clientId, expC, 'None', dd); + storage.setCookie('_jxx', clientId, expC, 'None', null); + storage.setCookie('_jxx', clientId, expC, 'None', dd); - storage.setCookie('_jxs', sessionId, expS, 'None', null); - storage.setCookie('_jxs', sessionId, expS, 'None', dd); + storage.setCookie('_jxxs', sessionId, expS, 'None', null); + storage.setCookie('_jxxs', sessionId, expS, 'None', dd); - storage.setDataInLocalStorage('_jx', clientId); - storage.setDataInLocalStorage('_jxs', sessionId); + storage.setDataInLocalStorage('_jxx', clientId); + storage.setDataInLocalStorage('_jxxs', sessionId); } catch (error) {} } @@ -47,14 +47,14 @@ function fetchIds_() { session_id_ls: '' }; try { - let tmp = storage.getCookie('_jx'); + let tmp = storage.getCookie('_jxx'); if (tmp) ret.client_id_c = tmp; - tmp = storage.getCookie('_jxs'); + tmp = storage.getCookie('_jxxs'); if (tmp) ret.session_id_c = tmp; - tmp = storage.getDataFromLocalStorage('_jx'); + tmp = storage.getDataFromLocalStorage('_jxx'); if (tmp) ret.client_id_ls = tmp; - tmp = storage.getDataFromLocalStorage('_jxs'); + tmp = storage.getDataFromLocalStorage('_jxxs'); if (tmp) ret.session_id_ls = tmp; } catch (error) {} return ret; diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js index bc44e594864..0c999f1cd51 100644 --- a/test/spec/modules/jixieBidAdapter_spec.js +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -199,16 +199,16 @@ describe('jixie Adapter', function () { let getCookieStub = sinon.stub(storage, 'getCookie'); let getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); getCookieStub - .withArgs('_jx') + .withArgs('_jxx') .returns(clientIdTest1_); getCookieStub - .withArgs('_jxs') + .withArgs('_jxxs') .returns(sessionIdTest1_); getLocalStorageStub - .withArgs('_jx') + .withArgs('_jxx') .returns(clientIdTest1_); getLocalStorageStub - .withArgs('_jxs') + .withArgs('_jxxs') .returns(sessionIdTest1_ ); let miscDimsStub = sinon.stub(jixieaux, 'getMiscDims'); @@ -528,10 +528,10 @@ describe('jixie Adapter', function () { let setCookieSpy = sinon.spy(storage, 'setCookie'); let setLocalStorageSpy = sinon.spy(storage, 'setDataInLocalStorage'); const result = spec.interpretResponse({body: responseBody_}, requestObj_) - expect(setLocalStorageSpy.calledWith('_jx', '43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); - expect(setLocalStorageSpy.calledWith('_jxs', '1600057934-43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); - expect(setCookieSpy.calledWith('_jxs', '1600057934-43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); - expect(setCookieSpy.calledWith('_jx', '43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); + expect(setLocalStorageSpy.calledWith('_jxx', '43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); + expect(setLocalStorageSpy.calledWith('_jxxs', '1600057934-43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); + expect(setCookieSpy.calledWith('_jxxs', '1600057934-43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); + expect(setCookieSpy.calledWith('_jxx', '43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); // video ad with vastUrl returned by adserver expect(result[0].requestId).to.equal('62847e4c696edcb') From 3fd47e278af345876640bcc26225092f67f3787f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ven=C3=A2ncio?= <45434454+jvnnc@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:21:10 -0300 Subject: [PATCH 031/569] removed localstorage bypass. (#8702) Co-authored-by: Jose --- modules/naveggIdSystem.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/modules/naveggIdSystem.js b/modules/naveggIdSystem.js index d7f5f712252..a292b42f144 100644 --- a/modules/naveggIdSystem.js +++ b/modules/naveggIdSystem.js @@ -34,17 +34,6 @@ function readnavIDFromCookie() { return storage.cookiesAreEnabled ? (storage.findSimilarCookies('nav') ? storage.findSimilarCookies('nav')[0] : null) : null; } -function readnvgnavFromLocalStorage() { - var i; - const query = /[nvga]{3}\d+/; - for (i in window.localStorage) { - if (i.match(query) || (!query && typeof i === 'string')) { - const naveggId = storage.getDataFromLocalStorage(i.match(query).input).split('|')[0] - return naveggId.split('_')[0]; - } - } -} - /** @type {Submodule} */ export const naveggIdSubmodule = { /** @@ -73,7 +62,7 @@ export const naveggIdSubmodule = { getId() { let naveggIdStringFromLocalStorage = null; if (storage.localStorageIsEnabled) { - naveggIdStringFromLocalStorage = readnaveggIdFromLocalStorage() || readnvgnavFromLocalStorage(); + naveggIdStringFromLocalStorage = readnaveggIdFromLocalStorage(); } const naveggIdString = naveggIdStringFromLocalStorage || readnaveggIDFromCookie() || readoldnaveggIDFromCookie() || readnvgIDFromCookie() || readnavIDFromCookie(); From a51ee4a51a92331f4335695b73cf327e156eba1b Mon Sep 17 00:00:00 2001 From: rimaburder-index <55195208+rimaburder-index@users.noreply.github.com> Date: Mon, 18 Jul 2022 12:31:29 -0400 Subject: [PATCH 032/569] Updated TTL for banner and video (#8704) --- modules/ixBidAdapter.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index 54bbf9ade40..790e895bedf 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -502,11 +502,8 @@ to `'ix'` across all ad units that bids are being requested for does not exceed ### Time-To-Live (TTL) -Banner bids from IX have a TTL of 300 seconds while video bids have a TTL of 1 hour, after which time they become -invalid. - -If an invalid bid wins, and its associated ad is rendered, it will not count -towards total impressions on IX's side. +Banner bids from Index have a TTL of 600 seconds while video bids have a TTL of three hours, after which time they become invalid. +**Note:** Index supports the `bid.exp` attribute in the bid response which allows our adapter to specify the maximum number of seconds allowed between the auction and billing notice. In the absence of the `bid.exp` attribute, the TTL provided above applies. FAQs ==== From fff550ca2b0dd0cf0c1fdd0dfce7c35667dc42a9 Mon Sep 17 00:00:00 2001 From: Jason Piros Date: Mon, 18 Jul 2022 10:21:34 -0700 Subject: [PATCH 033/569] consumableBidAdapter - add schain and coppa support (#8703) --- modules/consumableBidAdapter.js | 9 ++++ .../spec/modules/consumableBidAdapter_spec.js | 48 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 5c34fbe8c64..9a5f5f4675b 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -1,4 +1,5 @@ import { logWarn, createTrackPixelHtml, deepAccess, isArray, deepSetValue } from '../src/utils.js'; +import {config} from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'consumable'; @@ -66,6 +67,14 @@ export const spec = { data.ccpa = bidderRequest.uspConsent; } + if (bidderRequest && bidderRequest.schain) { + data.schain = bidderRequest.schain; + } + + if (config.getConfig('coppa')) { + data.coppa = true; + } + validBidRequests.map(bid => { const sizes = (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) || bid.sizes || []; const placement = Object.assign({ diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index 23851c0da40..4e860cb771d 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -33,6 +33,22 @@ const BIDDER_REQUEST_1 = { transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' } ], + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + }, + ] + }, gdprConsent: { consentString: 'consent-test', gdprApplies: false @@ -335,7 +351,37 @@ describe('Consumable BidAdapter', function () { let request = spec.buildRequests(BIDDER_REQUEST_1.bidRequest, BIDDER_REQUEST_1); expect(request.bidderRequest).to.equal(BIDDER_REQUEST_1); - }) + }); + + it('should contain schain if it exists in the bidRequest', function () { + let request = spec.buildRequests(BIDDER_REQUEST_1.bidRequest, BIDDER_REQUEST_1); + let data = JSON.parse(request.data); + + expect(data.schain).to.deep.equal(BIDDER_REQUEST_1.schain) + }); + + it('should not contain schain if it does not exist in the bidRequest', function () { + let request = spec.buildRequests(BIDDER_REQUEST_2.bidRequest, BIDDER_REQUEST_2); + let data = JSON.parse(request.data); + + expect(data.schain).to.be.undefined; + }); + + it('should contain coppa if configured', function () { + config.setConfig({ coppa: true }); + let request = spec.buildRequests(BIDDER_REQUEST_1.bidRequest, BIDDER_REQUEST_1); + let data = JSON.parse(request.data); + + expect(data.coppa).to.be.true; + }); + + it('should not contain coppa if not configured', function () { + config.setConfig({ coppa: false }); + let request = spec.buildRequests(BIDDER_REQUEST_1.bidRequest, BIDDER_REQUEST_1); + let data = JSON.parse(request.data); + + expect(data.coppa).to.be.undefined; + }); }); describe('interpretResponse validation', function () { it('response should have valid bidderCode', function () { From cd0529a6d8d307ba74bedac0cfcbd1dd2b267842 Mon Sep 17 00:00:00 2001 From: ccorbo Date: Mon, 18 Jul 2022 13:26:08 -0400 Subject: [PATCH 034/569] IX Bid Adapter : GPID and Bug Fix (#8698) * beginning work * wip * Build GPID manually when dp_ad_unit_code is present and gpid doesnt already exist * resolve conflict * remove pb ad slot * merge conflict resolved * fixed video warning Co-authored-by: Chris Corbo Co-authored-by: Love Sharma --- modules/ixBidAdapter.js | 34 ++++++++------ test/spec/modules/ixBidAdapter_spec.js | 64 ++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 136cf6f25f0..7d3b3a33a7d 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -1053,9 +1053,14 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { currentImpressionSize = encodeURIComponent(JSON.stringify({ impressionObjects })).length; } - const gpid = impressions[transactionIds[adUnitIndex]].gpid; + let gpid = impressions[transactionIds[adUnitIndex]].gpid; const dfpAdUnitCode = impressions[transactionIds[adUnitIndex]].dfp_ad_unit_code; - const tid = impressions[transactionIds[adUnitIndex]].tid + const tid = impressions[transactionIds[adUnitIndex]].tid; + const divId = impressions[transactionIds[adUnitIndex]].divId; + + if (!gpid && dfpAdUnitCode && divId) { + gpid = `${dfpAdUnitCode}#${divId}` + } if (impressionObjects.length && BANNER in impressionObjects[0]) { const { id, banner: { topframe } } = impressionObjects[0]; const _bannerImpression = { @@ -1135,7 +1140,6 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { const pbaAdSlot = impressions[transactionIds[adUnitIndex]].pbadslot const tagId = impressions[transactionIds[adUnitIndex]].tagId const adUnitCode = impressions[transactionIds[adUnitIndex]].adUnitCode - const divId = impressions[transactionIds[adUnitIndex]].divId if (pbaAdSlot || tagId || adUnitCode || divId) { const clonedRObject = deepClone(r); const requestSize = `${baseUrl}${parseQueryStringParameters({ ...payload, r: JSON.stringify(clonedRObject) })}`.length; @@ -1279,7 +1283,8 @@ function createNativeImps(validBidRequest, nativeImps) { nativeImps[validBidRequest.transactionId] = {}; nativeImps[validBidRequest.transactionId].ixImps = []; nativeImps[validBidRequest.transactionId].ixImps.push(imp); - nativeImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid') + nativeImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); + nativeImps[validBidRequest.transactionId].dfp_ad_unit_code = deepAccess(validBidRequest, 'ortb2Imp.ext.data.adserver.adslot'); nativeImps[validBidRequest.transactionId].pbadslot = deepAccess(validBidRequest, 'ortb2Imp.ext.data.pbadslot'); nativeImps[validBidRequest.transactionId].tagId = deepAccess(validBidRequest, 'params.tagId'); @@ -1302,6 +1307,7 @@ function createVideoImps(validBidRequest, videoImps) { videoImps[validBidRequest.transactionId].ixImps = []; videoImps[validBidRequest.transactionId].ixImps.push(imp); videoImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); + videoImps[validBidRequest.transactionId].dfp_ad_unit_code = deepAccess(validBidRequest, 'ortb2Imp.ext.data.adserver.adslot'); videoImps[validBidRequest.transactionId].pbadslot = deepAccess(validBidRequest, 'ortb2Imp.ext.data.pbadslot'); videoImps[validBidRequest.transactionId].tagId = deepAccess(validBidRequest, 'params.tagId'); @@ -1651,7 +1657,17 @@ export const spec = { } if (mediaTypeVideoRef && paramsVideoRef) { + const videoImp = bidToVideoImp(bid).video; const errorList = checkVideoParams(mediaTypeVideoRef, paramsVideoRef); + if (deepAccess(bid, 'mediaTypes.video.context') === OUTSTREAM && isIndexRendererPreferred(bid) && videoImp) { + const outstreamPlayerSize = [deepAccess(videoImp, 'w'), deepAccess(videoImp, 'h')]; + const isValidSize = outstreamPlayerSize[0] >= OUTSTREAM_MINIMUM_PLAYER_SIZE[0] && outstreamPlayerSize[1] >= OUTSTREAM_MINIMUM_PLAYER_SIZE[1]; + if (!isValidSize) { + logError(`IX Bid Adapter: ${outstreamPlayerSize} is an invalid size for IX outstream renderer`); + return false; + } + } + if (errorList.length) { errorList.forEach((err) => { logError(err, { bidder: BIDDER_CODE, code: ERROR_CODES.PROPERTY_NOT_INCLUDED }); @@ -1660,16 +1676,6 @@ export const spec = { } } - const videoImp = bidToVideoImp(bid).video; - if (deepAccess(bid, 'mediaTypes.video.context') === OUTSTREAM && isIndexRendererPreferred(bid) && videoImp) { - const outstreamPlayerSize = [deepAccess(videoImp, 'w'), deepAccess(videoImp, 'h')]; - const isValidSize = outstreamPlayerSize[0] >= OUTSTREAM_MINIMUM_PLAYER_SIZE[0] && outstreamPlayerSize[1] >= OUTSTREAM_MINIMUM_PLAYER_SIZE[1]; - if (!isValidSize) { - logError(`IX Bid Adapter: ${outstreamPlayerSize} is an invalid size for IX outstream renderer`); - return false; - } - } - return nativeMediaTypeValid(mediaTypeNativeRef); }, diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index a25e6b1c6a0..086ec305ccc 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1657,6 +1657,70 @@ describe('IndexexchangeAdapter', function () { expect(gpid).to.equal(GPID); }); + it('should still build gpid in request if ortb2Imp.ext.gpid does not exist', function () { + const AD_UNIT_CODE = '/1111/home'; + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + sinon.stub(utils, 'getGptSlotInfoForAdUnitCode') + .returns({ gptSlot: AD_UNIT_CODE, divId: 'adunit-code-3-div-id' }); + validBids[0].ortb2Imp = { + ext: { + data: { + adserver: { + name: 'gam', + adslot: AD_UNIT_CODE + } + } + } + } + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { ext: { gpid } } = JSON.parse(requests[0].data.r).imp[0]; + utils.getGptSlotInfoForAdUnitCode.restore(); + expect(gpid).to.equal(`${AD_UNIT_CODE}#adunit-code-3-div-id`); + }); + + it('should not build gpid if divid doesnt exist when ortb2Imp.ext.gpid does not exist', function () { + const AD_UNIT_CODE = '/1111/home'; + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + sinon.stub(utils, 'getGptSlotInfoForAdUnitCode') + .returns({ gptSlot: AD_UNIT_CODE }); + validBids[0].ortb2Imp = { + ext: { + data: { + adserver: { + name: 'gam', + adslot: AD_UNIT_CODE + } + } + } + } + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const imp = JSON.parse(requests[0].data.r).imp[0]; + utils.getGptSlotInfoForAdUnitCode.restore(); + expect(imp.ext.gpid).to.be.undefined; + expect(imp.ext.dfp_ad_unit_code).to.equal(AD_UNIT_CODE) + }); + + it('should not build gpid if dfp ad unit code / divid doesnt exist when ortb2Imp.ext.gpid does not exist', function () { + const AD_UNIT_CODE = '/1111/home'; + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + sinon.stub(utils, 'getGptSlotInfoForAdUnitCode') + .returns({}); + validBids[0].ortb2Imp = { + ext: { + data: { + adserver: { + name: 'gam', + } + } + } + } + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + + const imp = JSON.parse(requests[0].data.r).imp[0]; + utils.getGptSlotInfoForAdUnitCode.restore(); + expect(imp.ext).to.be.undefined; + }); + it('should send gpid in request if ortb2Imp.ext.gpid exists when no size present', function () { const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID_PARAM_NO_SIZE); validBids[0].ortb2Imp = { From 34e5b70deab0b0a0cfd9adb509de951255706bba Mon Sep 17 00:00:00 2001 From: Marcin Muras <47107445+mmuras@users.noreply.github.com> Date: Mon, 18 Jul 2022 20:50:25 +0200 Subject: [PATCH 035/569] AdOcean Bid Adapter: send slave ids which are taking part in auction. (#8694) --- modules/adoceanBidAdapter.js | 13 ++++++++++--- test/spec/modules/adoceanBidAdapter_spec.js | 5 ++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/adoceanBidAdapter.js b/modules/adoceanBidAdapter.js index bf5596ab22a..d74a78270b2 100644 --- a/modules/adoceanBidAdapter.js +++ b/modules/adoceanBidAdapter.js @@ -2,11 +2,15 @@ import { _each, parseSizesInput, isStr, isArray } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'adocean'; +const URL_SAFE_FIELDS = { + schain: true, + slaves: true +}; function buildEndpointUrl(emiter, payloadMap) { const payload = []; _each(payloadMap, function(v, k) { - payload.push(k + '=' + (k === 'schain' ? v : encodeURIComponent(v))); + payload.push(k + '=' + (URL_SAFE_FIELDS[k] ? v : encodeURIComponent(v))); }); const randomizedPart = Math.random().toString().slice(2); @@ -17,7 +21,8 @@ function buildRequest(masterBidRequests, masterId, gdprConsent) { let emiter; const payload = { id: masterId, - aosspsizes: [] + aosspsizes: [], + slaves: [] }; if (gdprConsent) { payload.gdpr_consent = gdprConsent.consentString || undefined; @@ -29,7 +34,7 @@ function buildRequest(masterBidRequests, masterId, gdprConsent) { } const bidIdMap = {}; - + const uniquePartLength = 10; _each(masterBidRequests, function(bid, slaveId) { if (!emiter) { emiter = bid.params.emiter; @@ -38,11 +43,13 @@ function buildRequest(masterBidRequests, masterId, gdprConsent) { const slaveSizes = parseSizesInput(bid.mediaTypes.banner.sizes).join('_'); const rawSlaveId = bid.params.slaveId.replace('adocean', ''); payload.aosspsizes.push(rawSlaveId + '~' + slaveSizes); + payload.slaves.push(rawSlaveId.slice(-uniquePartLength)); bidIdMap[slaveId] = bid.bidId; }); payload.aosspsizes = payload.aosspsizes.join('-'); + payload.slaves = payload.slaves.join(','); return { method: 'GET', diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js index ba691825112..080b5bd5d1d 100644 --- a/test/spec/modules/adoceanBidAdapter_spec.js +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -136,10 +136,12 @@ describe('AdoceanAdapter', function () { expect(request.url).to.include('gdpr_consent=' + bidderRequest.gdprConsent.consentString); }); - it('should attach sizes information to url', function () { + it('should attach sizes and slaves information to url', function () { let requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests[0].url).to.include('aosspsizes=myaozpniqismex~300x250_300x600'); + expect(requests[0].url).to.include('slaves=zpniqismex'); expect(requests[1].url).to.include('aosspsizes=myaozpniqismex~300x200_600x250'); + expect(requests[1].url).to.include('slaves=zpniqismex'); const differentSlavesBids = deepClone(bidRequests); differentSlavesBids[1].params.slaveId = 'adoceanmyaowafpdwlrks'; @@ -147,6 +149,7 @@ describe('AdoceanAdapter', function () { expect(requests.length).to.equal(1); expect(requests[0].url).to.include('aosspsizes=myaozpniqismex~300x250_300x600-myaowafpdwlrks~300x200_600x250'); expect((requests[0].url.match(/aosspsizes=/g) || []).length).to.equal(1); + expect(requests[0].url).to.include('slaves=zpniqismex,wafpdwlrks'); }); it('should attach schain parameter if available', function() { From 809c3799336ef8bd9bc65fa1f3f8a2b0c14e077f Mon Sep 17 00:00:00 2001 From: samous Date: Mon, 18 Jul 2022 23:57:34 +0200 Subject: [PATCH 036/569] BLIINK Bid Adapter: new api endpoint & ccpa support (#8625) * feat(multibid) add iframe cookie sync and multibid response * feat(multibid) change url endpoint * feat(multibid) replace requestId by transactionId * feat(multibid) add schain to request * refactor: bid request & response * feat(multibid) rename var and update tests * feat(multibid) update cookie sync url * feat(multibid) add uspConsent * feat(multibid) update readMe * feat(multibid) update tests * fix(refacto) changes after review * fix(refacto) use deepAcces if necessary * fix(refacto) use deepAcces * fix(refacto) refacto & use deepAcces * fix(refacto) fix default Co-authored-by: Niass Co-authored-by: Samous --- modules/bliinkBidAdapter.js | 313 ++++++-------- modules/bliinkBidAdapter.md | 7 +- test/spec/modules/bliinkBidAdapter_spec.js | 459 +++++++++++++++------ 3 files changed, 445 insertions(+), 334 deletions(-) diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js index 962a61efad5..495862013de 100644 --- a/modules/bliinkBidAdapter.js +++ b/modules/bliinkBidAdapter.js @@ -1,10 +1,12 @@ // eslint-disable-next-line prebid/validate-imports // eslint-disable-next-line prebid/validate-imports -import {registerBidder} from '../src/adapters/bidderFactory.js' +import { registerBidder } from '../src/adapters/bidderFactory.js' +import { config } from '../src/config.js' +import {_each, deepAccess, deepSetValue} from '../src/utils.js' export const BIDDER_CODE = 'bliink' -export const BLIINK_ENDPOINT_ENGINE = 'https://engine.bliink.io/delivery' -export const BLIINK_ENDPOINT_ENGINE_VAST = 'https://engine.bliink.io/vast' -export const BLIINK_ENDPOINT_COOKIE_SYNC = 'https://cookiesync.api.bliink.io' +export const BLIINK_ENDPOINT_ENGINE = 'https://engine.bliink.io/prebid' + +export const BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME = 'https://tag.bliink.io/usersync.html' export const META_KEYWORDS = 'keywords' export const META_DESCRIPTION = 'description' @@ -14,6 +16,13 @@ const BANNER = 'banner' const supportedMediaTypes = [BANNER, VIDEO] const aliasBidderCode = ['bk'] +/** + * @description get coppa value from config + */ +function getCoppa() { + return config.getConfig('coppa') === true ? 1 : 0; +} + export function getMetaList(name) { if (!name || name.length === 0) return [] @@ -52,7 +61,7 @@ export function getOneMetaValue(query) { return metaEl.content } - return null + return null; } export function getMetaValue(name) { @@ -75,79 +84,51 @@ export function getKeywords() { ] if (keywords && keywords.length > 0) { - return keywords - .filter((value) => value) - .map((value) => value.trim()) + return keywords.filter((value) => value).map((value) => value.trim()); } } - return [] -} - -export const parseXML = (content) => { - if (typeof content !== 'string' || content.length === 0) return null - - const parser = new DOMParser() - let xml; - - try { - xml = parser.parseFromString(content, 'text/xml') - } catch (e) {} - - if (xml && - xml.getElementsByTagName('VAST')[0] && - xml.getElementsByTagName('VAST')[0].tagName === 'VAST') { - return xml - } - - return null + return []; } /** * @param bidRequest - * @param bliinkCreative - * @return {{cpm, netRevenue: boolean, requestId, width: (*|number), currency, ttl: number, creativeId, height: (*|number)} & {mediaType: string, vastXml}} + * @return {({cpm, netRevenue: boolean, requestId, width: number, currency, ttl: number, creativeId, height: number}&{mediaType: string, vastXml})|null} */ -export const buildBid = (bidRequest, bliinkCreative) => { - if (!bidRequest && !bliinkCreative) return null - - const body = { - requestId: bidRequest.bidId, - currency: bliinkCreative.currency, - cpm: bliinkCreative.price, - creativeId: bliinkCreative.creativeId, - width: (bidRequest.sizes && bidRequest.sizes[0][0]) || 1, - height: (bidRequest.sizes && bidRequest.sizes[0][1]) || 1, - netRevenue: false, - ttl: 3600, - } - - // eslint-disable-next-line no-mixed-operators - if ((bliinkCreative) && bidRequest && - // eslint-disable-next-line no-mixed-operators - !bidRequest.bidId || - !bidRequest.sizes || - !bidRequest.params || - !(bidRequest.params.placement) - ) return null - - delete bidRequest['bids'] +export const buildBid = (bidResponse) => { + const mediaType = deepAccess(bidResponse, 'creative.media_type') + if (!mediaType) return null; - switch (bliinkCreative.media_type) { + let bid; + switch (mediaType) { case VIDEO: - return Object.assign(body, { - mediaType: VIDEO, - vastXml: bliinkCreative.content, - }) + const vastXml = deepAccess(bidResponse, 'creative.video.content'); + bid = { + vastXml, + mediaType: 'video', + vastUrl: 'data:text/xml;charset=utf-8;base64,' + btoa(vastXml.replace(/\\"/g, '"')) + }; + break; case BANNER: - return Object.assign(body, { - mediaType: BANNER, - ad: (bliinkCreative && bliinkCreative.content && bliinkCreative.content.creative && bliinkCreative.content.creative.adm) || '', - }) - default: + bid = { + ad: deepAccess(bidResponse, 'creative.banner.adm'), + mediaType: 'banner', + }; break; + default: + return null; } -} + return Object.assign(bid, { + cpm: bidResponse.price, + currency: bidResponse.currency || 'EUR', + creativeId: deepAccess(bidResponse, 'extras.deal_id'), + requestId: deepAccess(bidResponse, 'extras.transaction_id'), + width: deepAccess(bidResponse, `creative.${bid.mediaType}.width`) || 1, + height: deepAccess(bidResponse, `creative.${bid.mediaType}.height`) || 1, + ttl: 3600, + netRevenue: true, + }); +}; /** * @description Verify the the AdUnits.bids, respond with true (valid) or false (invalid). @@ -156,61 +137,58 @@ export const buildBid = (bidRequest, bliinkCreative) => { * @return boolean */ export const isBidRequestValid = (bid) => { - return !(!bid || !bid.params || !bid.params.placement || !bid.params.tagId) -} + return !!deepAccess(bid, 'params.tagId'); +}; /** * @description Takes an array of valid bid requests, all of which are guaranteed to have passed the isBidRequestValid() test. * - * @param _[] + * @param validBidRequests * @param bidderRequest - * @return {{ method: string, url: string } | null} + * @returns {null|{method: string, data: {gdprConsent: string, keywords: string, pageTitle: string, pageDescription: (*|string), pageUrl, gdpr: boolean, tags: *}, url: string}} */ -export const buildRequests = (_, bidderRequest) => { - if (!bidderRequest) return null +export const buildRequests = (validBidRequests, bidderRequest) => { + if (!validBidRequests || !bidderRequest || !bidderRequest.bids) return null - let data = { - pageUrl: bidderRequest.refererInfo.page, + const tags = bidderRequest.bids.map((bid) => { + return { + sizes: bid.sizes.map((size) => ({ w: size[0], h: size[1] })), + id: bid.params.tagId, + transactionId: bid.bidId, + mediaTypes: Object.keys(bid.mediaTypes), + imageUrl: deepAccess(bid, 'params.imageUrl', ''), + }; + }); + + let request = { + tags, + pageTitle: document.title, + pageUrl: deepAccess(bidderRequest, 'refererInfo.page'), pageDescription: getMetaValue(META_DESCRIPTION), keywords: getKeywords().join(','), - gdpr: false, - gdpr_consent: '', - pageTitle: document.title, + }; + const schain = deepAccess(validBidRequests[0], 'schain') + if (schain) { + request.schain = schain } - - const endPoint = bidderRequest.bids[0].params.placement === VIDEO ? BLIINK_ENDPOINT_ENGINE_VAST : BLIINK_ENDPOINT_ENGINE - - const params = { - bidderRequestId: bidderRequest.bidderRequestId, - bidderCode: bidderRequest.bidderCode, - bids: bidderRequest.bids, - // TODO: please do not send internal data structures over the network - refererInfo: bidderRequest.refererInfo.legacy, + const gdprConsent = deepAccess(bidderRequest, 'gdprConsent'); + if (!!gdprConsent && gdprConsent.gdprApplies) { + request.gdpr = true + deepSetValue(request, 'gdprConsent', gdprConsent.consentString); } - - if (bidderRequest.gdprConsent) { - data = Object.assign(data, { - gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies, - gdpr_consent: bidderRequest.gdprConsent.consentString - }) + if (config.getConfig('coppa')) { + request.coppa = 1 } - - if (bidderRequest.bids && bidderRequest.bids.length > 0 && bidderRequest.bids[0].sizes && bidderRequest.bids[0].sizes[0]) { - data = Object.assign(data, { - width: bidderRequest.bids[0].sizes[0][0], - height: bidderRequest.bids[0].sizes[0][1] - }) - - return { - method: 'GET', - url: `${endPoint}/${bidderRequest.bids[0].params.tagId}`, - data: data, - params: params, - } + if (bidderRequest.uspConsent) { + deepSetValue(request, 'uspConsent', bidderRequest.uspConsent); } - return null -} + return { + method: 'POST', + url: BLIINK_ENDPOINT_ENGINE, + data: request, + }; +}; /** * @description Parse the response (from buildRequests) and generate one or more bid objects. @@ -219,51 +197,15 @@ export const buildRequests = (_, bidderRequest) => { * @param request * @return */ -const interpretResponse = (serverResponse, request) => { - if ((serverResponse && serverResponse.mode === 'no-ad')) { - return [] - } - - const body = serverResponse.body - const serverBody = request.params - - const xml = parseXML(body) - - let creative; - - switch (serverBody.bids[0].params.placement) { - case xml && VIDEO: - const price = xml.getElementsByTagName('Price') && xml.getElementsByTagName('Price')[0] - const currency = xml.getElementsByTagName('Currency') && xml.getElementsByTagName('Currency')[0] - const creativeId = xml.getElementsByTagName('CreativeId') && xml.getElementsByTagName('CreativeId')[0] - - creative = { - content: body, - price: (price && price.textContent) || 0, - currency: (currency && currency.textContent) || 'EUR', - creativeId: creativeId || 0, - media_type: 'video', - } - - return buildBid(serverBody.bids[0], creative) - case BANNER: - if (body) { - creative = { - content: body, - price: body.price, - currency: body.currency, - creativeId: 0, - media_type: 'banner', - } - - return buildBid(serverBody.bids[0], creative) - } - - break - default: - break - } -} +const interpretResponse = (serverResponse) => { + const bodyResponse = deepAccess(serverResponse, 'body.bids') + if (!serverResponse.body || !bodyResponse) return [] + const bidResponses = []; + _each(bodyResponse, function (response) { + return bidResponses.push(buildBid(response)); + }); + return bidResponses.filter(bid => !!bid) +}; /** * @description If the publisher allows user-sync activity, the platform will call this function and the adapter may register pixels and/or iframe user syncs. For more information, see Registering User Syncs below @@ -272,54 +214,39 @@ const interpretResponse = (serverResponse, request) => { * @param gdprConsent * @return {[{type: string, url: string}]|*[]} */ -const getUserSyncs = (syncOptions, serverResponses, gdprConsent) => { - let syncs = [] - +const getUserSyncs = (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncs = []; if (syncOptions.pixelEnabled && serverResponses.length > 0) { + let gdprParams = '' + let uspConsentStr = '' + let apiVersion + let gdpr = false if (gdprConsent) { - const gdprParams = `consentString=${gdprConsent.consentString}` - const smartCallbackURL = encodeURIComponent(`${BLIINK_ENDPOINT_COOKIE_SYNC}/cookiesync?partner=smart&uid=[sas_uid]`) - const azerionCallbackURL = encodeURIComponent(`${BLIINK_ENDPOINT_COOKIE_SYNC}/cookiesync?partner=azerion&uid={PUB_USER_ID}`) - const appnexusCallbackURL = encodeURIComponent(`${BLIINK_ENDPOINT_COOKIE_SYNC}/cookiesync?partner=azerion&uid=$UID`) - return [ - { - type: 'script', - url: 'https://prg.smartadserver.com/ac?out=js&nwid=3392&siteid=305791&pgname=rg&fmtid=81127&tgt=[sas_target]&visit=m&tmstp=[timestamp]&clcturl=[countgo]' - }, - { - type: 'image', - url: `https://sync.smartadserver.com/getuid?nwid=3392&${gdprParams}&url=${smartCallbackURL}`, - }, - { - type: 'image', - url: `https://ad.360yield.com/server_match?partner_id=1531&${gdprParams}&r=${azerionCallbackURL}`, - }, - { - type: 'image', - url: `https://ads.stickyadstv.com/auto-user-sync?${gdprParams}`, - }, - { - type: 'image', - url: `https://cookiesync.api.bliink.io/getuid?url=https%3A%2F%2Fvisitor.omnitagjs.com%2Fvisitor%2Fsync%3Fuid%3D1625272249969090bb9d544bd6d8d645%26name%3DBLIINK%26visitor%3D%24UID%26external%3Dtrue&${gdprParams}`, - }, - { - type: 'image', - url: `https://cookiesync.api.bliink.io/getuid?url=https://pixel.advertising.com/ups/58444/sync?&gdpr=1&gdpr_consent=${gdprConsent.consentString}&redir=true&uid=$UID`, - }, - { - type: 'image', - url: `https://ups.analytics.yahoo.com/ups/58499/occ?gdpr=1&gdpr_consent=${gdprConsent.consentString}`, - }, + gdprParams = `&gdprConsent=${gdprConsent.consentString}`; + apiVersion = `&apiVersion=${gdprConsent.apiVersion}` + gdpr = Number( + gdprConsent.gdprApplies) + } + if (uspConsent) { + uspConsentStr = `&uspConsent=${uspConsent}`; + } + let sync; + if (syncOptions.iframeEnabled) { + sync = [ { - type: 'image', - url: `https://secure.adnxs.com/getuid?${appnexusCallbackURL}`, + type: 'iframe', + url: `${BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME}?gdpr=${gdpr}&coppa=${getCoppa()}${uspConsentStr}${gdprParams}${apiVersion}`, }, - ] + ]; + } else { + sync = deepAccess(serverResponses[0], 'body.userSyncs'); } + + return sync; } return syncs; -} +}; /** * @type {{interpretResponse: interpretResponse, code: string, aliases: string[], getUserSyncs: getUserSyncs, buildRequests: buildRequests, onTimeout: onTimeout, onSetTargeting: onSetTargeting, isBidRequestValid: isBidRequestValid, onBidWon: onBidWon}} @@ -332,6 +259,6 @@ export const spec = { buildRequests, interpretResponse, getUserSyncs, -} +}; -registerBidder(spec) +registerBidder(spec); diff --git a/modules/bliinkBidAdapter.md b/modules/bliinkBidAdapter.md index af7aee3a1ae..48b95a10ebb 100644 --- a/modules/bliinkBidAdapter.md +++ b/modules/bliinkBidAdapter.md @@ -3,10 +3,10 @@ ``` Module Name: BLIINK Bidder Adapter Module Type: Bidder Adapter -Maintainer: samuel@bliink.io | jonathan@bliink.io +Maintainer: samuel@bliink.io | ibrahima@bliink.io gdpr_supported: true tcf2_supported: true -media_types: banner, native, video +media_types: banner, video ``` # Description @@ -30,7 +30,6 @@ const adUnits = [ { bidder: 'bliink', params: { - placement: 'banner', tagId: '41' } } @@ -58,7 +57,6 @@ const adUnits = [ bidder: 'bliink', params: { tagId: '41', - placement: 'video', } } ] @@ -85,7 +83,6 @@ const adUnits = [ bidder: 'bliink', params: { tagId: '41', - placement: 'video', } } ] diff --git a/test/spec/modules/bliinkBidAdapter_spec.js b/test/spec/modules/bliinkBidAdapter_spec.js index 748ed2c8279..b8994b86847 100644 --- a/test/spec/modules/bliinkBidAdapter_spec.js +++ b/test/spec/modules/bliinkBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai' -import { spec, buildBid, BLIINK_ENDPOINT_ENGINE, parseXML, getMetaList } from 'modules/bliinkBidAdapter.js' +import { spec, buildBid, BLIINK_ENDPOINT_ENGINE, getMetaList, BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME } from 'modules/bliinkBidAdapter.js' /** * @description Mockup bidRequest @@ -33,6 +33,7 @@ const getConfigBid = (placement) => { crumbs: { pubcid: '55ffadc5-051f-428d-8ecc-dc585e0bde0d' }, + sizes: [[300, 250]], mediaTypes: { banner: { sizes: [ @@ -51,13 +52,55 @@ const getConfigBid = (placement) => { placement: placement, tagId: '14f30eca-85d2-11e8-9eed-0242ac120007' }, - sizes: [ - [300, 250] - ], src: 'client', transactionId: 'cc6678c4-9746-4082-b9e2-d8065d078ebf' } } +const getConfigBannerBid = () => { + return { + creative: { + banner: { + adm: '', + height: 250, + width: 300, + }, + media_type: 'banner', + creativeId: 125, + }, + price: 1, + id: '810', + token: 'token', + mode: 'rtb', + extras: { + deal_id: '34567erty', + transaction_id: '2def0c5b2a7f6e', + }, + currency: 'EUR', + } +} +const getConfigVideoBid = () => { + return { + creative: { + video: { + content: + '', + height: 250, + width: 300, + }, + media_type: 'video', + creativeId: 0, + }, + price: 1, + id: '8121', + token: 'token', + mode: 'rtb', + extras: { + deal_id: '34567ertyRTY', + transaction_id: '2def0c5b2a7f6e', + }, + currency: 'EUR', + } +} /** * @description Mockup response from engine.bliink.io/xxxx @@ -80,33 +123,30 @@ const getConfigBid = (placement) => { */ const getConfigCreative = () => { return { - ad_id: 5648, - price: 1, + ad: '', + mediaType: 'banner', + cpm: 4, currency: 'EUR', - media_type: 'banner', - category: 1, - id: 2825, - creativeId: 2825, - type: 1, - viewability_duration: 1, - viewability_percent_in_view: 30, - content: { - creative: { - adm: '', - } - } + creativeId: '34567erty', + width: 300, + height: 250, + ttl: 3600, + netRevenue: true, } } -const getConfigCreativeVideo = () => { +const getConfigCreativeVideo = (isNoVast) => { return { - ad_id: 5648, - price: 1, + mediaType: 'video', + vastXml: isNoVast ? '' : '', + cpm: 0, currency: 'EUR', - media_type: 'video', - category: 1, - creativeId: 2825, - content: '' + creativeId: '34567ertyaza', + requestId: '6a204ce130280d', + width: 300, + height: 250, + ttl: 3600, + netRevenue: true, } } @@ -120,14 +160,11 @@ const getConfigBuildRequest = (placement) => { bidderCode: 'bliink', bids: [getConfigBid(placement)], refererInfo: { + canonicalUrl: null, + isAmp: false, + numIframes: 0, + reachedTop: true, page: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', - legacy: { - canonicalUrl: null, - isAmp: false, - numIframes: 0, - reachedTop: true, - referer: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', - } }, } @@ -158,8 +195,9 @@ const getConfigInterpretResponse = (noAd = false) => { return { body: { - creative: getConfigCreative(), + ...getConfigCreative(), mode: 'ad', + transactionId: '2def0c5b2a7f6e', token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjgxNzA4MzEsImlhdCI6MTYyNzU2NjAzMSwiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6IjM1YmU1NDNjLTNkZTQtNGQ1Yy04N2NjLWIzYzEyOGZiYzU0MCIsIm5ldHdvcmtJZCI6MjEsInNpdGVJZCI6NTksInRhZ0lkIjo1OSwiY29va2llSWQiOiJjNGU4MWVhOS1jMjhmLTQwZDItODY1ZC1hNjQzZjE1OTcyZjUiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwiaXAiOiI3OC4xMjIuNzUuNzIiLCJ0aW1lIjoxNjI3NTY2MDMxLCJsb2NhdGlvbiI6eyJsYXRpdHVkZSI6NDguOTczOSwibG9uZ2l0dWRlIjozLjMxMTMsInJlZ2lvbiI6IkhERiIsImNvdW50cnkiOiJGUiIsImNpdHkiOiJTYXVsY2hlcnkiLCJ6aXBDb2RlIjoiMDIzMTAiLCJkZXBhcnRtZW50IjoiMDIifSwiY2l0eSI6IlNhdWxjaGVyeSIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTEuMC40NDcyLjEyNCBTYWZhcmkvNTM3LjM2In0sImdkcHIiOnsiaGFzQ29uc2VudCI6dHJ1ZX0sIndpbiI6ZmFsc2UsImFkSWQiOjU2NDgsImFkdmVydGlzZXJJZCI6MSwiY2FtcGFpZ25JZCI6MSwiY3JlYXRpdmVJZCI6MjgyNSwiZXJyb3IiOmZhbHNlfX0.-UefQH4G0k-RJGemBYffs-KL7EEwma2Wuwgk2xnpij8' }, headers: {}, @@ -171,7 +209,7 @@ const getConfigInterpretResponse = (noAd = false) => { * @param noAd * @return {{body: string} | {mode: string, message: string}} */ -const getConfigInterpretResponseRTB = (noAd = false) => { +const getConfigInterpretResponseRTB = (noAd = false, isInvalidVast = false) => { if (noAd) { return { message: 'invalid tag', @@ -179,20 +217,51 @@ const getConfigInterpretResponseRTB = (noAd = false) => { } } + const validVast = ` + + + + BLIINK + https://vast.bliink.io/p/508379d0-9f65-4198-8ba5-f61f2b51224f.xml + https://e.api.bliink.io/e?name=vast-error&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzQwMzA1MjcsImlhdCI6MTYzMzQyNTcyNywiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6ImE2NjJjZGJmLTkzNDYtNDI0MS1iMTU0LTJhOTc2OTg0NjNmOSIsIm5ldHdvcmtJZCI6MjUsInNpdGVJZCI6MTQzLCJ0YWdJZCI6MTI3MSwiY29va2llSWQiOiIwNWFhN2UwMi05MzgzLTQ1NGYtOTJmZC1jOTE2YWNlMmUyZjYiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwicmVmZXJyZXIiOiJodHRwOi8vbG9jYWxob3N0OjgxODEvaW50ZWdyYXRpb25FeGFtcGxlcy9ncHQvYmxpaW5rLWluc3RyZWFtLmh0bWwiLCJwYWdlVXJsIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgxL2ludGVncmF0aW9uRXhhbXBsZXMvZ3B0L2JsaWluay1pbnN0cmVhbS5odG1sIiwiaXAiOiIzMS4zOS4xNDEuMTQwIiwidGltZSI6MTYzMzQyNTcyNywibG9jYXRpb24iOnsibGF0aXR1ZGUiOjQ4Ljk0MjIsImxvbmdpdHVkZSI6Mi41MDM5LCJyZWdpb24iOiJJREYiLCJjb3VudHJ5IjoiRlIiLCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsInppcENvZGUiOiI5MzYwMCIsImRlcGFydG1lbnQiOiI5MyJ9LCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTMuMC40NTc3LjYzIFNhZmFyaS81MzcuMzYiLCJjb250ZW50Q2xhc3NpZmljYXRpb24iOnsiYnJhbmRzYWZlIjpmYWxzZX19LCJnZHByIjp7Imhhc0NvbnNlbnQiOnRydWV9LCJ3aW4iOmZhbHNlLCJhZElkIjo1NzkzLCJhZHZlcnRpc2VySWQiOjEsImNhbXBhaWduSWQiOjEsImNyZWF0aXZlSWQiOjExOTQsImVycm9yIjpmYWxzZX19.nJSJPKovg0_jSHtLdrMPDqesAIlFKCuXPXYxpsyWBDw + https://e.api.bliink.io/e?name=impression&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzQwMzA1MjcsImlhdCI6MTYzMzQyNTcyNywiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6ImE2NjJjZGJmLTkzNDYtNDI0MS1iMTU0LTJhOTc2OTg0NjNmOSIsIm5ldHdvcmtJZCI6MjUsInNpdGVJZCI6MTQzLCJ0YWdJZCI6MTI3MSwiY29va2llSWQiOiIwNWFhN2UwMi05MzgzLTQ1NGYtOTJmZC1jOTE2YWNlMmUyZjYiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwicmVmZXJyZXIiOiJodHRwOi8vbG9jYWxob3N0OjgxODEvaW50ZWdyYXRpb25FeGFtcGxlcy9ncHQvYmxpaW5rLWluc3RyZWFtLmh0bWwiLCJwYWdlVXJsIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgxL2ludGVncmF0aW9uRXhhbXBsZXMvZ3B0L2JsaWluay1pbnN0cmVhbS5odG1sIiwiaXAiOiIzMS4zOS4xNDEuMTQwIiwidGltZSI6MTYzMzQyNTcyNywibG9jYXRpb24iOnsibGF0aXR1ZGUiOjQ4Ljk0MjIsImxvbmdpdHVkZSI6Mi41MDM5LCJyZWdpb24iOiJJREYiLCJjb3VudHJ5IjoiRlIiLCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsInppcENvZGUiOiI5MzYwMCIsImRlcGFydG1lbnQiOiI5MyJ9LCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTMuMC40NTc3LjYzIFNhZmFyaS81MzcuMzYiLCJjb250ZW50Q2xhc3NpZmljYXRpb24iOnsiYnJhbmRzYWZlIjpmYWxzZX19LCJnZHByIjp7Imhhc0NvbnNlbnQiOnRydWV9LCJ3aW4iOmZhbHNlLCJhZElkIjo1NzkzLCJhZHZlcnRpc2VySWQiOjEsImNhbXBhaWduSWQiOjEsImNyZWF0aXZlSWQiOjExOTQsImVycm9yIjpmYWxzZX19.nJSJPKovg0_jSHtLdrMPDqesAIlFKCuXPXYxpsyWBDw + 1EUR + + + + ` + const invalidVast = ` + + + + + + ` + return { - body: ` - - - - BLIINK - https://vast.bliink.io/p/508379d0-9f65-4198-8ba5-f61f2b51224f.xml - https://e.api.bliink.io/e?name=vast-error&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzQwMzA1MjcsImlhdCI6MTYzMzQyNTcyNywiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6ImE2NjJjZGJmLTkzNDYtNDI0MS1iMTU0LTJhOTc2OTg0NjNmOSIsIm5ldHdvcmtJZCI6MjUsInNpdGVJZCI6MTQzLCJ0YWdJZCI6MTI3MSwiY29va2llSWQiOiIwNWFhN2UwMi05MzgzLTQ1NGYtOTJmZC1jOTE2YWNlMmUyZjYiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwicmVmZXJyZXIiOiJodHRwOi8vbG9jYWxob3N0OjgxODEvaW50ZWdyYXRpb25FeGFtcGxlcy9ncHQvYmxpaW5rLWluc3RyZWFtLmh0bWwiLCJwYWdlVXJsIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgxL2ludGVncmF0aW9uRXhhbXBsZXMvZ3B0L2JsaWluay1pbnN0cmVhbS5odG1sIiwiaXAiOiIzMS4zOS4xNDEuMTQwIiwidGltZSI6MTYzMzQyNTcyNywibG9jYXRpb24iOnsibGF0aXR1ZGUiOjQ4Ljk0MjIsImxvbmdpdHVkZSI6Mi41MDM5LCJyZWdpb24iOiJJREYiLCJjb3VudHJ5IjoiRlIiLCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsInppcENvZGUiOiI5MzYwMCIsImRlcGFydG1lbnQiOiI5MyJ9LCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTMuMC40NTc3LjYzIFNhZmFyaS81MzcuMzYiLCJjb250ZW50Q2xhc3NpZmljYXRpb24iOnsiYnJhbmRzYWZlIjpmYWxzZX19LCJnZHByIjp7Imhhc0NvbnNlbnQiOnRydWV9LCJ3aW4iOmZhbHNlLCJhZElkIjo1NzkzLCJhZHZlcnRpc2VySWQiOjEsImNhbXBhaWduSWQiOjEsImNyZWF0aXZlSWQiOjExOTQsImVycm9yIjpmYWxzZX19.nJSJPKovg0_jSHtLdrMPDqesAIlFKCuXPXYxpsyWBDw - https://e.api.bliink.io/e?name=impression&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzQwMzA1MjcsImlhdCI6MTYzMzQyNTcyNywiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6ImE2NjJjZGJmLTkzNDYtNDI0MS1iMTU0LTJhOTc2OTg0NjNmOSIsIm5ldHdvcmtJZCI6MjUsInNpdGVJZCI6MTQzLCJ0YWdJZCI6MTI3MSwiY29va2llSWQiOiIwNWFhN2UwMi05MzgzLTQ1NGYtOTJmZC1jOTE2YWNlMmUyZjYiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwicmVmZXJyZXIiOiJodHRwOi8vbG9jYWxob3N0OjgxODEvaW50ZWdyYXRpb25FeGFtcGxlcy9ncHQvYmxpaW5rLWluc3RyZWFtLmh0bWwiLCJwYWdlVXJsIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgxL2ludGVncmF0aW9uRXhhbXBsZXMvZ3B0L2JsaWluay1pbnN0cmVhbS5odG1sIiwiaXAiOiIzMS4zOS4xNDEuMTQwIiwidGltZSI6MTYzMzQyNTcyNywibG9jYXRpb24iOnsibGF0aXR1ZGUiOjQ4Ljk0MjIsImxvbmdpdHVkZSI6Mi41MDM5LCJyZWdpb24iOiJJREYiLCJjb3VudHJ5IjoiRlIiLCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsInppcENvZGUiOiI5MzYwMCIsImRlcGFydG1lbnQiOiI5MyJ9LCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTMuMC40NTc3LjYzIFNhZmFyaS81MzcuMzYiLCJjb250ZW50Q2xhc3NpZmljYXRpb24iOnsiYnJhbmRzYWZlIjpmYWxzZX19LCJnZHByIjp7Imhhc0NvbnNlbnQiOnRydWV9LCJ3aW4iOmZhbHNlLCJhZElkIjo1NzkzLCJhZHZlcnRpc2VySWQiOjEsImNhbXBhaWduSWQiOjEsImNyZWF0aXZlSWQiOjExOTQsImVycm9yIjpmYWxzZX19.nJSJPKovg0_jSHtLdrMPDqesAIlFKCuXPXYxpsyWBDw - 1EUR - - - - ` + body: { bids: [ + { + 'creative': { + 'video': { + 'content': isInvalidVast ? invalidVast : validVast, + 'height': 250, + 'width': 300 + }, + 'media_type': 'video', + 'creativeId': 0, + }, + 'price': 0, + 'id': '8121', + 'token': 'token', + 'mode': 'rtb', + 'extras': { + 'deal_id': '34567ertyaza', + 'transaction_id': '2def0c5b2a7f6e' + }, + 'currency': 'EUR' + } + ], + userSyncs: []} } } @@ -261,31 +330,6 @@ describe('BLIINK Adapter getMetaList', function() { * @description Array of tests used in describe function below * @type {[{args: {fn: (string|Document)}, want: string, title: string}, {args: {fn: (string|Document)}, want: string, title: string}]} */ -const testsParseXML = [ - { - title: 'Should return null, if content length equal to 0', - args: { - fn: parseXML('') - }, - want: null, - }, - { - title: 'Should return null, if content isnt string', - args: { - fn: parseXML({}) - }, - want: null, - }, -] - -describe('BLIINK Adapter parseXML', function() { - for (const test of testsParseXML) { - it(test.title, () => { - const res = test.args.fn - expect(res).to.eql(test.want) - }) - } -}) /** * @@ -334,29 +378,67 @@ describe('BLIINK Adapter isBidRequestValid', function() { } }) +const vastXml = getConfigInterpretResponseRTB().body.bids[0].creative.video.content + const testsInterpretResponse = [ { title: 'Should construct bid for video instream', args: { - fn: spec.interpretResponse(getConfigInterpretResponseRTB(false), getConfigBuildRequest('video')) + fn: spec.interpretResponse(getConfigInterpretResponseRTB(false)) }, - want: { + want: [{ cpm: 0, currency: 'EUR', height: 250, width: 300, - creativeId: 0, + creativeId: '34567ertyaza', mediaType: 'video', - netRevenue: false, + netRevenue: true, requestId: '2def0c5b2a7f6e', ttl: 3600, - vastXml: getConfigInterpretResponseRTB().body, - } + vastXml, + vastUrl: 'data:text/xml;charset=utf-8;base64,' + btoa(vastXml.replace(/\\"/g, '"')) + }] }, { title: 'ServerResponse with message: invalid tag, return empty array', args: { - fn: spec.interpretResponse(getConfigInterpretResponse(true), getConfigBuildRequest('banner')) + fn: spec.interpretResponse(getConfigInterpretResponse(true)) + }, + want: [] + }, + { + title: 'ServerResponse with mediaType banner', + args: { + fn: spec.interpretResponse({body: {bids: [getConfigBannerBid()]}}), + }, + want: [{ + ad: '', + cpm: 1, + creativeId: '34567erty', + currency: 'EUR', + height: 250, + mediaType: 'banner', + netRevenue: true, + requestId: '2def0c5b2a7f6e', + ttl: 3600, + width: 300 + }] + }, + { + title: 'ServerResponse with unhandled mediaType, return empty array', + args: { + fn: spec.interpretResponse({body: {bids: [{...getConfigBannerBid(), + creative: { + unknown: { + adm: '', + height: 250, + width: 300, + }, + media_type: 'unknown', + creativeId: 125, + requestId: '2def0c5b2a7f6e', + }}]}}), }, want: [] }, @@ -391,6 +473,7 @@ describe('BLIINK Adapter interpretResponse', function() { * } * }, want, title: string}]} */ + const testsBuildBid = [ { title: 'Should return null if no bid passed in parameters', @@ -409,7 +492,7 @@ const testsBuildBid = [ { title: 'input data respect the output model for video', args: { - fn: buildBid(getConfigBid('video'), getConfigCreativeVideo()) + fn: buildBid(getConfigVideoBid('video'), getConfigCreativeVideo()) }, want: { requestId: getConfigBid('video').bidId, @@ -418,16 +501,47 @@ const testsBuildBid = [ mediaType: 'video', width: 300, height: 250, - creativeId: getConfigCreativeVideo().creativeId, - netRevenue: false, - vastXml: getConfigCreativeVideo().content, + creativeId: getConfigVideoBid().extras.deal_id, + netRevenue: true, + vastXml: getConfigCreativeVideo().vastXml, + vastUrl: 'data:text/xml;charset=utf-8;base64,' + btoa(getConfigCreativeVideo().vastXml.replace(/\\"/g, '"')), + ttl: 3600, + } + }, + { + title: 'use default height width output model for video', + args: { + fn: buildBid({...getConfigVideoBid('video'), + creative: { + video: { + content: + '', + height: null, + width: null, + }, + media_type: 'video', + creativeId: getConfigVideoBid().extras.deal_id, + requestId: '2def0c5b2a7f6e', + }}, getConfigCreativeVideo()) + }, + want: { + requestId: getConfigBid('video').bidId, + cpm: 1, + currency: 'EUR', + mediaType: 'video', + width: 1, + height: 1, + creativeId: getConfigVideoBid().extras.deal_id, + netRevenue: true, + vastXml: getConfigCreativeVideo().vastXml, + vastUrl: 'data:text/xml;charset=utf-8;base64,' + btoa(getConfigCreativeVideo().vastXml.replace(/\\"/g, '"')), ttl: 3600, } }, { title: 'input data respect the output model for banner', args: { - fn: buildBid(getConfigBid('banner'), getConfigCreative()) + fn: buildBid(getConfigBannerBid()) }, want: { requestId: getConfigBid('banner').bidId, @@ -436,10 +550,10 @@ const testsBuildBid = [ mediaType: 'banner', width: 300, height: 250, - creativeId: getConfigCreative().id, - ad: getConfigCreative().content.creative.adm, + creativeId: getConfigCreative().creativeId, + ad: getConfigBannerBid().creative.banner.adm, ttl: 3600, - netRevenue: false, + netRevenue: true, } } ] @@ -471,23 +585,27 @@ const testsBuildRequests = [ fn: spec.buildRequests([], getConfigBuildRequest('banner')) }, want: { - method: 'GET', - url: `${BLIINK_ENDPOINT_ENGINE}/${getConfigBuildRequest('banner').bids[0].params.tagId}`, - params: { - bidderRequestId: getConfigBuildRequest('banner').bidderRequestId, - bidderCode: getConfigBuildRequest('banner').bidderCode, - bids: getConfigBuildRequest('banner').bids, - refererInfo: getConfigBuildRequest('banner').refererInfo.legacy - }, + method: 'POST', + url: BLIINK_ENDPOINT_ENGINE, data: { - gdpr: false, - gdpr_consent: '', - height: 250, - width: 300, keywords: '', pageDescription: '', pageTitle: '', pageUrl: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', + tags: [ + { + transactionId: '2def0c5b2a7f6e', + id: '14f30eca-85d2-11e8-9eed-0242ac120007', + imageUrl: '', + mediaTypes: ['banner'], + sizes: [ + { + h: 250, + w: 300, + }, + ], + }, + ] } } }, @@ -502,23 +620,83 @@ const testsBuildRequests = [ })) }, want: { - method: 'GET', - url: `${BLIINK_ENDPOINT_ENGINE}/${getConfigBuildRequest('banner').bids[0].params.tagId}`, - params: { - bidderRequestId: getConfigBuildRequest('banner').bidderRequestId, - bidderCode: getConfigBuildRequest('banner').bidderCode, - bids: getConfigBuildRequest('banner').bids, - refererInfo: getConfigBuildRequest('banner').refererInfo.legacy - }, + method: 'POST', + url: BLIINK_ENDPOINT_ENGINE, data: { gdpr: true, - gdpr_consent: 'XXXX', + gdprConsent: 'XXXX', pageDescription: '', pageTitle: '', keywords: '', pageUrl: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', - height: 250, - width: 300, + tags: [ + { + transactionId: '2def0c5b2a7f6e', + id: '14f30eca-85d2-11e8-9eed-0242ac120007', + imageUrl: '', + mediaTypes: ['banner'], + sizes: [ + { + h: 250, + w: 300, + }, + ], + }, + ] + } + } + }, + { + title: 'Should build request width schain if exists', + args: { + fn: spec.buildRequests([{schain: { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'ssp.test', + sid: '00001', + hp: 1 + }] + }}], Object.assign(getConfigBuildRequest('banner'), { + gdprConsent: { + gdprApplies: true, + consentString: 'XXXX' + }, + })) + }, + want: { + method: 'POST', + url: BLIINK_ENDPOINT_ENGINE, + data: { + gdpr: true, + gdprConsent: 'XXXX', + pageDescription: '', + pageTitle: '', + keywords: '', + pageUrl: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', + schain: { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'ssp.test', + sid: '00001', + hp: 1 + }] + }, + tags: [ + { + transactionId: '2def0c5b2a7f6e', + id: '14f30eca-85d2-11e8-9eed-0242ac120007', + imageUrl: '', + mediaTypes: ['banner'], + sizes: [ + { + h: 250, + w: 300, + }, + ], + }, + ] } } } @@ -533,7 +711,7 @@ describe('BLIINK Adapter buildRequests', function() { } }) -const getSyncOptions = (pixelEnabled = true, iframeEnabled = 'false') => { +const getSyncOptions = (pixelEnabled = true, iframeEnabled = false) => { return { pixelEnabled, iframeEnabled @@ -543,7 +721,15 @@ const getSyncOptions = (pixelEnabled = true, iframeEnabled = 'false') => { const getServerResponses = () => { return [ { - body: '', + body: {bids: [], + userSyncs: [ { + type: 'script', + url: 'https://prg.smartadserver.com/ac?out=js&nwid=3392&siteid=305791&pgname=rg&fmtid=81127&tgt=[sas_target]&visit=m&tmstp=[timestamp]&clcturl=[countgo]' + }, + { + type: 'image', + url: 'https://sync.smartadserver.com/getuid?nwid=3392&consentString=XXX&url=https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dsmart%26uid%3D%5Bsas_uid%5D' + }]}, } ] } @@ -551,7 +737,8 @@ const getServerResponses = () => { const getGdprConsent = () => { return { gdprApplies: 1, - consentString: 'XXX' + consentString: 'XXX', + apiVersion: 2 } } @@ -569,39 +756,39 @@ const testsGetUserSyncs = [ { type: 'image', url: 'https://sync.smartadserver.com/getuid?nwid=3392&consentString=XXX&url=https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dsmart%26uid%3D%5Bsas_uid%5D' - }, - { - type: 'image', - url: 'https://ad.360yield.com/server_match?partner_id=1531&consentString=XXX&r=https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dazerion%26uid%3D%7BPUB_USER_ID%7D', - }, - { - type: 'image', - url: 'https://ads.stickyadstv.com/auto-user-sync?consentString=XXX', - }, - { - type: 'image', - url: 'https://cookiesync.api.bliink.io/getuid?url=https%3A%2F%2Fvisitor.omnitagjs.com%2Fvisitor%2Fsync%3Fuid%3D1625272249969090bb9d544bd6d8d645%26name%3DBLIINK%26visitor%3D%24UID%26external%3Dtrue&consentString=XXX', - }, - { - type: 'image', - url: 'https://cookiesync.api.bliink.io/getuid?url=https://pixel.advertising.com/ups/58444/sync?&gdpr=1&gdpr_consent=XXX&redir=true&uid=$UID', - }, + } + ] + }, + { + title: 'Should return iframe cookie sync if iframeEnabled', + args: { + fn: spec.getUserSyncs(getSyncOptions(true, true), getServerResponses(), getGdprConsent()) + }, + want: [ { - type: 'image', - url: 'https://ups.analytics.yahoo.com/ups/58499/occ?gdpr=1&gdpr_consent=XXX', + type: 'iframe', + url: `${BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME}?gdpr=${getGdprConsent().gdprApplies}&coppa=0&gdprConsent=${getGdprConsent().consentString}&apiVersion=${getGdprConsent().apiVersion}` }, + ] + }, + { + title: 'ccpa', + args: { + fn: spec.getUserSyncs(getSyncOptions(true, true), getServerResponses(), getGdprConsent(), 'ccpa-consent') + }, + want: [ { - type: 'image', - url: 'https://secure.adnxs.com/getuid?https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dazerion%26uid%3D%24UID', + type: 'iframe', + url: `${BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME}?gdpr=${getGdprConsent().gdprApplies}&coppa=0&uspConsent=ccpa-consent&gdprConsent=${getGdprConsent().consentString}&apiVersion=${getGdprConsent().apiVersion}` }, ] }, { - title: 'Should not have gdpr consent', + title: 'Should output sync if no gdprConsent', args: { fn: spec.getUserSyncs(getSyncOptions(), getServerResponses()) }, - want: [] + want: getServerResponses()[0].body.userSyncs } ] From 99e38773bbd5171cb8d58f69c7410d12211bb0d6 Mon Sep 17 00:00:00 2001 From: Dejan Grbavcic Date: Tue, 19 Jul 2022 12:33:50 +0200 Subject: [PATCH 037/569] TargetVideo Bid Adapter: Add GDPR/USP support (#8589) * TargetVideo bid adapter * TargetVideo bid adapter * TargetVideo bid adapter * TargetVideo Bid Adapter: Add GDPR/USP support * TargetVideo Bid Adapter: Add GDPR/USP support tests --- modules/targetVideoBidAdapter.js | 18 ++++++++ .../modules/targetVideoBidAdapter_spec.js | 43 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js index 4deb63b6426..f8cb58218cd 100644 --- a/modules/targetVideoBidAdapter.js +++ b/modules/targetVideoBidAdapter.js @@ -42,6 +42,24 @@ export const spec = { }, schain: schain }; + + if (bidderRequest && bidderRequest.gdprConsent) { + payload.gdpr_consent = { + consent_string: bidderRequest.gdprConsent.consentString, + consent_required: bidderRequest.gdprConsent.gdprApplies + }; + + if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { + let ac = bidderRequest.gdprConsent.addtlConsent; + let acStr = ac.substring(ac.indexOf('~') + 1); + payload.gdpr_consent.addtl_consent = acStr.split('.').map(id => parseInt(id, 10)); + } + } + + if (bidderRequest && bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent + } + return formatRequest(payload, bidderRequest); }, diff --git a/test/spec/modules/targetVideoBidAdapter_spec.js b/test/spec/modules/targetVideoBidAdapter_spec.js index 0ce6f0fb70d..adbc982e509 100644 --- a/test/spec/modules/targetVideoBidAdapter_spec.js +++ b/test/spec/modules/targetVideoBidAdapter_spec.js @@ -93,4 +93,47 @@ describe('TargetVideo Bid Adapter', function() { expect(bid.ad).to.include('') expect(bid.ad).to.include('initPlayer') }); + + it('Test GDPR consent information is present in the request', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'targetVideo', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true, + addtlConsent: '1~7.12.35.62.66.70.89.93.108' + } + }; + bidderRequest.bids = bannerRequest; + + const request = spec.buildRequests(bannerRequest, bidderRequest); + expect(request.options).to.deep.equal({withCredentials: true}); + const payload = JSON.parse(request.data); + + expect(payload.gdpr_consent).to.exist; + expect(payload.gdpr_consent.consent_string).to.exist.and.to.equal(consentString); + expect(payload.gdpr_consent.consent_required).to.exist.and.to.be.true; + expect(payload.gdpr_consent.addtl_consent).to.exist.and.to.deep.equal([7, 12, 35, 62, 66, 70, 89, 93, 108]); + }); + + it('Test US Privacy string is present in the request', function() { + let consentString = '1YA-'; + let bidderRequest = { + 'bidderCode': 'targetVideo', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'uspConsent': consentString + }; + bidderRequest.bids = bannerRequest; + + const request = spec.buildRequests(bannerRequest, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.us_privacy).to.exist; + expect(payload.us_privacy).to.exist.and.to.equal(consentString); + }); }); From 1a9f4c0629384d865dce981edd26f98b1f829cfa Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 19 Jul 2022 11:06:35 -0700 Subject: [PATCH 038/569] Core & multiple modules: refactor usage of Promise to avoid deferrals when possible (#8626) * Core & multiple modules: refactor usage of Promise to avoid deferrals when possible * Unhandled rejection handling * Improve tests --- modules/currency.js | 4 +- modules/userId/index.js | 60 ++-- src/auction.js | 11 +- src/consentHandler.js | 22 +- src/debugging.js | 8 +- src/hook.js | 4 +- src/utils.js | 5 +- src/utils/promise.js | 117 ++++++-- test/helpers/consentData.js | 3 +- test/helpers/syncPromise.js | 71 ----- test/spec/auctionmanager_spec.js | 7 +- test/spec/debugging_spec.js | 4 +- test/spec/modules/idxIdSystem_spec.js | 2 +- test/spec/modules/parrableIdSystem_spec.js | 3 +- test/spec/modules/userId_spec.js | 17 +- test/spec/unit/pbjs_api_spec.js | 6 +- test/spec/unit/utils/promise_spec.js | 321 ++++++++++++++++++--- 17 files changed, 471 insertions(+), 194 deletions(-) delete mode 100644 test/helpers/syncPromise.js diff --git a/modules/currency.js b/modules/currency.js index 289ec8fbf69..392817b5822 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -5,7 +5,7 @@ import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; import { getHook } from '../src/hook.js'; -import {promiseControls} from '../src/utils/promise.js'; +import {defer} from '../src/utils/promise.js'; const DEFAULT_CURRENCY_RATE_URL = 'https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json?date=$$TODAY$$'; const CURRENCY_RATE_PRECISION = 4; @@ -24,7 +24,7 @@ var defaultRates; export const ready = (() => { let ctl; function reset() { - ctl = promiseControls(); + ctl = defer(); } reset(); return {done: () => ctl.resolve(), reset, promise: () => ctl.promise} diff --git a/modules/userId/index.js b/modules/userId/index.js index 7edc2862b57..ecc494846ee 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -152,7 +152,7 @@ import { isEmpty } from '../../src/utils.js'; import {getPPID as coreGetPPID} from '../../src/adserver.js'; -import {promiseControls} from '../../src/utils/promise.js'; +import {defer, GreedyPromise} from '../../src/utils/promise.js'; import {hasPurpose1Consent} from '../../src/utils/gpdr.js'; const MODULE_NAME = 'User ID'; @@ -504,15 +504,11 @@ function addIdDataToAdUnitBids(adUnits, submodules) { }); } -function delayFor(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - const INIT_CANCELED = {}; -function idSystemInitializer({delay = delayFor} = {}) { - const startInit = promiseControls(); - const startCallbacks = promiseControls(); +function idSystemInitializer({delay = GreedyPromise.timeout} = {}) { + const startInit = defer(); + const startCallbacks = defer(); let cancel; let initialized = false; @@ -520,8 +516,8 @@ function idSystemInitializer({delay = delayFor} = {}) { if (cancel != null) { cancel.reject(INIT_CANCELED); } - cancel = promiseControls(); - return Promise.race([promise, cancel.promise]); + cancel = defer(); + return GreedyPromise.race([promise, cancel.promise]); } // grab a reference to global vars so that the promise chains remain isolated; @@ -540,7 +536,7 @@ function idSystemInitializer({delay = delayFor} = {}) { } let done = cancelAndTry( - Promise.all([hooksReady, startInit.promise]) + GreedyPromise.all([hooksReady, startInit.promise]) .then(() => gdprDataHandler.promise) .then(checkRefs((consentData) => { initSubmodules(initModules, allModules, consentData); @@ -549,7 +545,7 @@ function idSystemInitializer({delay = delayFor} = {}) { .then(checkRefs(() => { const modWithCb = initModules.filter(item => isFn(item.callback)); if (modWithCb.length) { - return new Promise((resolve) => processSubmoduleCallbacks(modWithCb, resolve)); + return new GreedyPromise((resolve) => processSubmoduleCallbacks(modWithCb, resolve)); } })) ); @@ -573,7 +569,7 @@ function idSystemInitializer({delay = delayFor} = {}) { }); } } - if (refresh) { + if (refresh && initialized) { done = cancelAndTry( done .catch(() => null) @@ -588,7 +584,7 @@ function idSystemInitializer({delay = delayFor} = {}) { return sm.callback != null; }); if (cbModules.length) { - return new Promise((resolve) => processSubmoduleCallbacks(cbModules, resolve)); + return new GreedyPromise((resolve) => processSubmoduleCallbacks(cbModules, resolve)); } })) ); @@ -621,8 +617,8 @@ function getPPID() { * @param {Object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids. * @param {function} fn required; The next function in the chain, used by hook.js */ -export function requestBidsHook(fn, reqBidsConfigObj, {delay = delayFor} = {}) { - Promise.race([ +export function requestBidsHook(fn, reqBidsConfigObj, {delay = GreedyPromise.timeout} = {}) { + GreedyPromise.race([ getUserIdsAsync(), delay(auctionDelay) ]).then(() => { @@ -767,7 +763,16 @@ function refreshUserIds({submoduleNames} = {}, callback) { */ function getUserIdsAsync() { - return initIdSystem().then(() => getUserIds(), (e) => e === INIT_CANCELED ? getUserIdsAsync() : Promise.reject(e)); + return initIdSystem().then( + () => getUserIds(), + (e) => + e === INIT_CANCELED + // there's a pending refresh - because GreedyPromise runs this synchronously, we are now in the middle + // of canceling the previous init, before the refresh logic has had a chance to run. + // Use a "normal" Promise to clear the stack and let it complete (or this will just recurse infinitely) + ? Promise.resolve().then(getUserIdsAsync) + : GreedyPromise.reject(e) + ); } /** @@ -914,6 +919,8 @@ function updateSubmodules() { return; } // do this to avoid reprocessing submodules + // TODO: the logic does not match the comment - addedSubmodules is always a copy of submoduleRegistry + // (if it did it would not be correct - it's not enough to find new modules, as others may have been removed or changed) const addedSubmodules = submoduleRegistry.filter(i => !find(submodules, j => j.name === i.name)); submodules.splice(0, submodules.length); @@ -949,6 +956,17 @@ export function attachIdSystem(submodule) { if (!find(submoduleRegistry, i => i.name === submodule.name)) { submoduleRegistry.push(submodule); updateSubmodules(); + // TODO: a test case wants this to work even if called after init (the setConfig({userId})) + // so we trigger a refresh. But is that even possible outside of tests? + initIdSystem({refresh: true, submoduleNames: [submodule.name]}); + } +} + +function normalizePromise(fn) { + // for public methods that return promises, make sure we return a "normal" one - to avoid + // exposing confusing stack traces + return function() { + return Promise.resolve(fn.apply(this, arguments)); } } @@ -957,7 +975,7 @@ export function attachIdSystem(submodule) { * so a callback is added to fire after the consentManagement module. * @param {{getConfig:function}} config */ -export function init(config, {delay = delayFor} = {}) { +export function init(config, {delay = GreedyPromise.timeout} = {}) { ppidSource = undefined; submodules = []; configRegistry = []; @@ -1002,10 +1020,10 @@ export function init(config, {delay = delayFor} = {}) { // exposing getUserIds function in global-name-space so that userIds stored in Prebid can be used by external codes. (getGlobal()).getUserIds = getUserIds; (getGlobal()).getUserIdsAsEids = getUserIdsAsEids; - (getGlobal()).getEncryptedEidsForSource = getEncryptedEidsForSource; + (getGlobal()).getEncryptedEidsForSource = normalizePromise(getEncryptedEidsForSource); (getGlobal()).registerSignalSources = registerSignalSources; - (getGlobal()).refreshUserIds = refreshUserIds; - (getGlobal()).getUserIdsAsync = getUserIdsAsync; + (getGlobal()).refreshUserIds = normalizePromise(refreshUserIds); + (getGlobal()).getUserIdsAsync = normalizePromise(getUserIdsAsync); (getGlobal()).getUserIdsAsEidBySource = getUserIdsAsEidBySource; } diff --git a/src/auction.js b/src/auction.js index fb9c6302e74..a1d6261201f 100644 --- a/src/auction.js +++ b/src/auction.js @@ -76,6 +76,7 @@ import {bidderSettings} from './bidderSettings.js'; import * as events from './events.js' import adapterManager from './adapterManager.js'; import CONSTANTS from './constants.json'; +import {GreedyPromise} from './utils/promise.js'; const { syncUsers } = userSync; @@ -384,9 +385,9 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM function waitFor(requestId, result) { if (ready[requestId] == null) { - ready[requestId] = Promise.resolve(); + ready[requestId] = GreedyPromise.resolve(); } - ready[requestId] = ready[requestId].then(() => Promise.resolve(result).catch(() => {})) + ready[requestId] = ready[requestId].then(() => GreedyPromise.resolve(result).catch(() => {})) } function guard(bidderRequest, fn) { @@ -398,9 +399,9 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM const wait = ready[bidderRequest.bidderRequestId]; const orphanWait = ready['']; // also wait for "orphan" responses that are not associated with any request if ((wait != null || orphanWait != null) && timeRemaining > 0) { - Promise.race([ - new Promise((resolve) => setTimeout(resolve, timeRemaining)), - Promise.resolve(orphanWait).then(() => wait) + GreedyPromise.race([ + GreedyPromise.timeout(timeRemaining), + GreedyPromise.resolve(orphanWait).then(() => wait) ]).then(fn); } else { fn(); diff --git a/src/consentHandler.js b/src/consentHandler.js index a56d06c8c90..861a9894a2c 100644 --- a/src/consentHandler.js +++ b/src/consentHandler.js @@ -1,10 +1,10 @@ import {isStr, timestamp} from './utils.js'; +import {defer, GreedyPromise} from './utils/promise.js'; export class ConsentHandler { #enabled; #data; - #promise; - #resolve; + #defer; #ready; generatedTime; @@ -12,17 +12,17 @@ export class ConsentHandler { this.reset(); } + #resolve(data) { + this.#ready = true; + this.#data = data; + this.#defer.resolve(data); + } + /** * reset this handler (mainly for tests) */ reset() { - this.#promise = new Promise((resolve) => { - this.#resolve = (data) => { - this.#ready = true; - this.#data = data; - resolve(data); - }; - }); + this.#defer = defer(); this.#enabled = false; this.#data = null; this.#ready = false; @@ -56,12 +56,12 @@ export class ConsentHandler { */ get promise() { if (this.#ready) { - return Promise.resolve(this.#data); + return GreedyPromise.resolve(this.#data); } if (!this.#enabled) { this.#resolve(null); } - return this.#promise; + return this.#defer.promise; } setConsentData(data, time = timestamp()) { diff --git a/src/debugging.js b/src/debugging.js index aae92197096..817ec66e320 100644 --- a/src/debugging.js +++ b/src/debugging.js @@ -4,6 +4,7 @@ import {getGlobal} from './prebidGlobal.js'; import {logMessage, prefixLog} from './utils.js'; import {createBid} from './bidfactory.js'; import {loadExternalScript} from './adloader.js'; +import {GreedyPromise} from './utils/promise.js'; export const DEBUG_KEY = '__$$PREBID_GLOBAL$$_debugging__'; @@ -12,7 +13,7 @@ function isDebuggingInstalled() { } function loadScript(url) { - return new Promise((resolve) => { + return new GreedyPromise((resolve) => { loadExternalScript(url, 'debugging', resolve); }); } @@ -21,7 +22,8 @@ export function debuggingModuleLoader({alreadyInstalled = isDebuggingInstalled, let loading = null; return function () { if (loading == null) { - loading = new Promise((resolve, reject) => { + loading = new GreedyPromise((resolve, reject) => { + // run this in a 0-delay timeout to give installedModules time to be populated setTimeout(() => { if (alreadyInstalled()) { resolve(); @@ -44,7 +46,7 @@ export function debuggingControls({load = debuggingModuleLoader(), hook = getHoo let promise = null; let enabled = false; function waitForDebugging(next, ...args) { - return (promise || Promise.resolve()).then(() => next.apply(this, args)) + return (promise || GreedyPromise.resolve()).then(() => next.apply(this, args)) } function enable() { if (!enabled) { diff --git a/src/hook.js b/src/hook.js index c3f897a6bca..226c49daeae 100644 --- a/src/hook.js +++ b/src/hook.js @@ -1,11 +1,11 @@ import funHooks from 'fun-hooks/no-eval/index.js'; -import {promiseControls} from './utils/promise.js'; +import {defer} from './utils/promise.js'; export let hook = funHooks({ ready: funHooks.SYNC | funHooks.ASYNC | funHooks.QUEUE }); -const readyCtl = promiseControls(); +const readyCtl = defer(); hook.ready = (() => { const ready = hook.ready; return function () { diff --git a/src/utils.js b/src/utils.js index ede171936cc..b53475af912 100644 --- a/src/utils.js +++ b/src/utils.js @@ -2,6 +2,7 @@ import { config } from './config.js'; import clone from 'just-clone'; import {find, includes} from './polyfill.js'; import CONSTANTS from './constants.json'; +import {GreedyPromise} from './utils/promise.js'; export { default as deepAccess } from 'dlv/index.js'; export { default as deepSetValue } from 'dset'; @@ -487,7 +488,7 @@ export function hasOwn(objectToCheck, propertyToCheckFor) { * @param {HTMLElement} [doc] * @param {HTMLElement} [target] * @param {Boolean} [asLastChildChild] -* @return {HTMLElement} +* @return {HTML Element} */ export function insertElement(elm, doc, target, asLastChildChild) { doc = doc || document; @@ -517,7 +518,7 @@ export function insertElement(elm, doc, target, asLastChildChild) { */ export function waitForElementToLoad(element, timeout) { let timer = null; - return new Promise((resolve) => { + return new GreedyPromise((resolve) => { const onLoad = function() { element.removeEventListener('load', onLoad); element.removeEventListener('error', onLoad); diff --git a/src/utils/promise.js b/src/utils/promise.js index 8b7f9d9d40d..229056786d2 100644 --- a/src/utils/promise.js +++ b/src/utils/promise.js @@ -1,36 +1,111 @@ const SUCCESS = 0; const FAIL = 1; -const RESULT = 2; /** - * @returns a {promise, resolve, reject} trio where `promise` is resolved by calling `resolve` or `reject`. + * A version of Promise that runs callbacks synchronously when it can (i.e. after it's been fulfilled or rejected). */ -export function promiseControls({promiseFactory = (resolver) => new Promise(resolver)} = {}) { - const status = {}; +export class GreedyPromise extends Promise { + #result; + #callbacks; + #parent = null; + + /** + * Convenience wrapper for setTimeout; takes care of returning an already fulfilled GreedyPromise when the delay is zero. + * + * @param {Number} delayMs delay in milliseconds + * @returns {GreedyPromise} a promise that resolves (to undefined) in `delayMs` milliseconds + */ + static timeout(delayMs = 0) { + return new GreedyPromise((resolve) => { + delayMs === 0 ? resolve() : setTimeout(resolve, delayMs); + }); + } - function finisher(slot) { - return function (val) { - if (typeof status[slot] === 'function') { - status[slot](val); - } else if (!status[slot]) { - status[slot] = true; - status[RESULT] = val; + constructor(resolver) { + const result = []; + const callbacks = []; + function handler(type, resolveFn) { + return function (value) { + if (!result.length) { + result.push(type, value); + while (callbacks.length) callbacks.shift()(); + resolveFn(value); + } + } + } + super( + typeof resolver !== 'function' + ? resolver // let super throw an error + : (resolve, reject) => { + const rejectHandler = handler(FAIL, reject); + const resolveHandler = (() => { + const done = handler(SUCCESS, resolve); + return value => + typeof value?.then === 'function' ? value.then(done, rejectHandler) : done(value); + })(); + try { + resolver(resolveHandler, rejectHandler); + } catch (e) { + rejectHandler(e); + } + } + ); + this.#result = result; + this.#callbacks = callbacks; + } + then(onSuccess, onError) { + if (typeof onError === 'function') { + // if an error handler is provided, attach a dummy error handler to super, + // and do the same for all promises without an error handler that precede this one in a chain. + // This is to avoid unhandled rejection events / warnings for errors that were, in fact, handled; + // since we are not using super's callback mechanisms we need to make it aware of this separately. + let node = this; + while (node) { + super.then.call(node, null, () => null); + const next = node.#parent; + node.#parent = null; // since we attached a handler already, we are no longer interested in what will happen later in the chain + node = next; } } + const result = this.#result; + const res = new GreedyPromise((resolve, reject) => { + const continuation = () => { + let value = result[1]; + let [handler, resolveFn] = result[0] === SUCCESS ? [onSuccess, resolve] : [onError, reject]; + if (typeof handler === 'function') { + try { + value = handler(value); + } catch (e) { + reject(e); + return; + } + resolveFn = resolve; + } + resolveFn(value); + } + result.length ? continuation() : this.#callbacks.push(continuation); + }); + res.#parent = this; + return res; } +} + +/** + * @returns a {promise, resolve, reject} trio where `promise` is resolved by calling `resolve` or `reject`. + */ +export function defer({promiseFactory = (resolver) => new GreedyPromise(resolver)} = {}) { + function invoker(delegate) { + return (val) => delegate(val); + } + + let resolveFn, rejectFn; return { promise: promiseFactory((resolve, reject) => { - if (status[SUCCESS] != null) { - resolve(status[RESULT]); - } else if (status[FAIL] != null) { - reject(status[RESULT]); - } else { - status[SUCCESS] = resolve; - status[FAIL] = reject; - } + resolveFn = resolve; + rejectFn = reject; }), - resolve: finisher(SUCCESS), - reject: finisher(FAIL) + resolve: invoker(resolveFn), + reject: invoker(rejectFn) } } diff --git a/test/helpers/consentData.js b/test/helpers/consentData.js index 17ddc583f88..b59388d67f2 100644 --- a/test/helpers/consentData.js +++ b/test/helpers/consentData.js @@ -1,6 +1,7 @@ import {gdprDataHandler} from 'src/adapterManager.js'; +import {GreedyPromise} from '../../src/utils/promise.js'; export function mockGdprConsent(sandbox, getConsentData = () => null) { - sandbox.stub(gdprDataHandler, 'promise').get(() => Promise.resolve(getConsentData())); + sandbox.stub(gdprDataHandler, 'promise').get(() => GreedyPromise.resolve(getConsentData())); sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(getConsentData) } diff --git a/test/helpers/syncPromise.js b/test/helpers/syncPromise.js deleted file mode 100644 index 99361bd716e..00000000000 --- a/test/helpers/syncPromise.js +++ /dev/null @@ -1,71 +0,0 @@ -const orig = {}; -['resolve', 'reject', 'all', 'race', 'allSettled'].forEach((k) => orig[k] = Promise[k].bind(Promise)) - -// Callbacks attached through Promise.resolve(value).then(...) will usually -// not execute immediately even if `value` is immediately available. This -// breaks tests that were written before promises even though they are semantically still valid. -// They can be made to work by making promises quasi-synchronous. - -export function SyncPromise(value, fail = false) { - if (value instanceof SyncPromise) { - return value; - } else if (typeof value === 'object' && typeof value.then === 'function') { - return orig.resolve(value); - } else { - Object.assign(this, { - then: function (cb, err) { - const handler = fail ? err : cb; - if (handler != null) { - return new SyncPromise(handler(value)); - } else { - return this; - } - }, - catch: function (cb) { - if (fail) { - return new SyncPromise(cb(value)) - } else { - return this; - } - }, - finally: function (cb) { - cb(); - return this; - }, - __value: fail ? {status: 'rejected', reason: value} : {status: 'fulfilled', value} - }) - } -} - -Object.assign(SyncPromise, { - resolve: (val) => new SyncPromise(val), - reject: (val) => new SyncPromise(val, true), - race: (promises) => promises.find((p) => p instanceof SyncPromise) || orig.race(promises), - allSettled: (promises) => { - if (promises.every((p) => p instanceof SyncPromise)) { - return new SyncPromise(promises.map((p) => p.__value)) - } else { - return orig.allSettled(promises); - } - }, - all: (promises) => { - if (promises.every((p) => p instanceof SyncPromise)) { - return SyncPromise.allSettled(promises).then((result) => { - const err = result.find((r) => r.status === 'rejected'); - if (err != null) { - return new SyncPromise(err.reason, true); - } else { - return new SyncPromise(result.map((r) => r.value)) - } - }) - } else { - return orig.all(promises); - } - } -}) - -export function synchronizePromise(sandbox) { - Object.keys(orig).forEach((k) => { - sandbox.stub(window.Promise, k).callsFake(SyncPromise[k]); - }) -} diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 48b915c77f6..49ae13c43cc 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -21,7 +21,6 @@ import {auctionManager} from '../../src/auctionManager.js'; import 'modules/debugging/index.js' // some tests look for debugging side effects import {AuctionIndex} from '../../src/auctionIndex.js'; import {expect} from 'chai'; -import {synchronizePromise} from '../helpers/syncPromise.js'; var assert = require('assert'); @@ -134,7 +133,7 @@ function mockAjaxBuilder() { } describe('auctionmanager.js', function () { - let indexAuctions, indexStub, promiseSandbox; + let indexAuctions, indexStub before(() => { // hooks are global and their side effects depend on what has been loaded @@ -150,13 +149,10 @@ describe('auctionmanager.js', function () { indexAuctions = []; indexStub = sinon.stub(auctionManager, 'index'); indexStub.get(() => new AuctionIndex(() => indexAuctions)); - promiseSandbox = sinon.createSandbox(); - synchronizePromise(promiseSandbox); }); afterEach(() => { indexStub.restore(); - promiseSandbox.restore(); }); describe('getKeyValueTargetingPairs', function () { @@ -1458,7 +1454,6 @@ describe('auctionmanager.js', function () { } beforeEach(() => { - promiseSandbox.restore(); bids = [ mockBid({bidderCode: BIDDER_CODE1}), mockBid({bidderCode: BIDDER_CODE}) diff --git a/test/spec/debugging_spec.js b/test/spec/debugging_spec.js index 34eafbda61a..8408ceec367 100644 --- a/test/spec/debugging_spec.js +++ b/test/spec/debugging_spec.js @@ -1,6 +1,6 @@ import {ready, loadSession, getConfig, reset, debuggingModuleLoader, debuggingControls} from '../../src/debugging.js'; import {getGlobal} from '../../src/prebidGlobal.js'; -import {promiseControls} from '../../src/utils/promise.js'; +import {defer} from '../../src/utils/promise.js'; import funHooks from 'fun-hooks/no-eval/index.js'; describe('Debugging', () => { @@ -67,7 +67,7 @@ describe('Debugging', () => { let debugging, loader, hook, hookRan; beforeEach(() => { - loader = promiseControls(); + loader = defer(); hookRan = false; hook = funHooks()('sync', () => { hookRan = true }); debugging = debuggingControls({load: sinon.stub().returns(loader.promise), hook}); diff --git a/test/spec/modules/idxIdSystem_spec.js b/test/spec/modules/idxIdSystem_spec.js index 7d008ef0d28..56e1c709c8b 100644 --- a/test/spec/modules/idxIdSystem_spec.js +++ b/test/spec/modules/idxIdSystem_spec.js @@ -94,8 +94,8 @@ describe('IDx ID System', () => { adUnits = [getAdUnitMock()]; init(config); setSubmoduleRegistry([idxIdSubmodule]); - config.setConfig(getConfigMock()); getCookieStub.withArgs(IDX_COOKIE_NAME).returns(IDX_COOKIE_STORED); + config.setConfig(getConfigMock()); }); afterEach(() => { diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 506aa4914c5..55287e0bfec 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -655,7 +655,6 @@ describe('Parrable ID System', function() { writeParrableCookie({ eid: P_COOKIE_EID, ibaOptout: true }); init(config); setSubmoduleRegistry([parrableIdSubmodule]); - config.setConfig(getConfigMock()); }); afterEach(function() { @@ -665,6 +664,7 @@ describe('Parrable ID System', function() { }); it('when a stored Parrable ID exists it is added to bids', function(done) { + config.setConfig(getConfigMock()); requestBidsHook(function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { @@ -691,6 +691,7 @@ describe('Parrable ID System', function() { it('supplies an optout reason when the EID is missing due to CCPA non-consent', function(done) { // the ID system itself will not write a cookie with an EID when CCPA=true writeParrableCookie({ ccpaOptout: true }); + config.setConfig(getConfigMock()); requestBidsHook(function() { adUnits.forEach(unit => { diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 49c07bb1334..486018d41c4 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -466,6 +466,7 @@ describe('User ID', function () { describe('refreshing before init is complete', () => { const MOCK_ID = {'MOCKID': '1111'}; let mockIdCallback; + let startInit; beforeEach(() => { mockIdCallback = sinon.stub(); @@ -480,7 +481,7 @@ describe('User ID', function () { }; init(config); setSubmoduleRegistry([mockIdSystem]); - config.setConfig({ + startInit = () => config.setConfig({ userSync: { auctionDelay: 10, userIds: [{ @@ -492,6 +493,7 @@ describe('User ID', function () { }); it('should still resolve promises returned by getUserIdsAsync', () => { + startInit(); let result = null; getGlobal().getUserIdsAsync().then((val) => { result = val; }); return clearStack().then(() => { @@ -506,6 +508,7 @@ describe('User ID', function () { it('should not stop auctions', (done) => { // simulate an infinite `auctionDelay`; refreshing should still allow the auction to continue // as soon as ID submodules have completed init + startInit(); requestBidsHook(() => { done(); }, {adUnits: [getAdUnitMock()]}, {delay: delay()}); @@ -519,6 +522,7 @@ describe('User ID', function () { it('should not get stuck when init fails', () => { const err = new Error(); mockIdCallback.callsFake(() => { throw err; }); + startInit(); return getGlobal().getUserIdsAsync().catch((e) => expect(e).to.equal(err) ); @@ -2531,7 +2535,6 @@ describe('User ID', function () { // init id system attachIdSystem(mockIdSystem); - config.setConfig(userIdConfig); }); afterEach(function () { @@ -2542,6 +2545,8 @@ describe('User ID', function () { coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + config.setConfig(userIdConfig); + let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits @@ -2556,6 +2561,8 @@ describe('User ID', function () { coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 60 * 1000).toUTCString()), expStr); + config.setConfig(userIdConfig); + let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits @@ -2572,6 +2579,8 @@ describe('User ID', function () { setStoredConsentData(); + config.setConfig(userIdConfig); + let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits @@ -2588,6 +2597,8 @@ describe('User ID', function () { setStoredConsentData({...consentData, consentString: 'different'}); + config.setConfig(userIdConfig); + let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits @@ -2604,6 +2615,8 @@ describe('User ID', function () { setStoredConsentData({...consentData}); + config.setConfig(userIdConfig); + let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 1abb89f3d24..ca646743147 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -16,7 +16,6 @@ import * as auctionModule from 'src/auction.js'; import { registerBidder } from 'src/adapters/bidderFactory.js'; import { _sendAdToCreative } from 'src/secureCreatives.js'; import {find} from 'src/polyfill.js'; -import {synchronizePromise} from '../../helpers/syncPromise.js'; import * as pbjsModule from 'src/prebid.js'; import {hook} from '../../../src/hook.js'; import {reset as resetDebugging} from '../../../src/debugging.js'; @@ -195,7 +194,7 @@ window.apntag = { } describe('Unit: Prebid Module', function () { - let bidExpiryStub, promiseSandbox; + let bidExpiryStub before(() => { hook.ready(); @@ -204,15 +203,12 @@ describe('Unit: Prebid Module', function () { }); beforeEach(function () { - promiseSandbox = sinon.createSandbox(); - synchronizePromise(promiseSandbox); bidExpiryStub = sinon.stub(filters, 'isBidNotExpired').callsFake(() => true); configObj.setConfig({ useBidCache: true }); resetAuctionState(); }); afterEach(function() { - promiseSandbox.restore(); $$PREBID_GLOBAL$$.adUnits = []; bidExpiryStub.restore(); configObj.setConfig({ useBidCache: false }); diff --git a/test/spec/unit/utils/promise_spec.js b/test/spec/unit/utils/promise_spec.js index ee2d55bf9a5..a931b8bc9c4 100644 --- a/test/spec/unit/utils/promise_spec.js +++ b/test/spec/unit/utils/promise_spec.js @@ -1,49 +1,294 @@ -import {promiseControls} from '../../../../src/utils/promise.js'; +import {GreedyPromise, defer} from '../../../../src/utils/promise.js'; -describe('promiseControls', () => { - function lazyControls() { - // NOTE: here we are testing that calling resolve / reject works correctly regardless of whether the - // browser runs promise resolvers before / after returning control to the code; e.g. with the following: - // - // new Promise(() => console.log('1')); console.log('2') - // - // it seems that the browser will output '1', then '2' - but is it always guaranteed to do so? - // it's not clear from MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) - // so here we make sure it works in both cases. - - return promiseControls({ - promiseFactory: (r) => ({ - then: function () { - const p = new Promise(r); - return p.then.apply(p, arguments); +describe('GreedyPromise', () => { + it('throws when resolver is not a function', () => { + expect(() => new GreedyPromise()).to.throw(); + }) + + Object.entries({ + 'resolved': (use) => new GreedyPromise((resolve) => use(resolve)), + 'rejected': (use) => new GreedyPromise((_, reject) => use(reject)) + }).forEach(([t, makePromise]) => { + it(`runs callbacks immediately when ${t}`, () => { + let cbRan = false; + const cb = () => { cbRan = true }; + let resolver; + makePromise((fn) => { resolver = fn }).then(cb, cb); + resolver(); + expect(cbRan).to.be.true; + }) + }); + + describe('unhandled rejections', () => { + let unhandled, done, stop; + + function reset(expectUnhandled) { + let pending = expectUnhandled; + let resolver; + unhandled.reset(); + unhandled.callsFake(() => { + pending--; + if (pending === 0) { + resolver(); } }) - }) - } + done = new Promise((resolve) => { + resolver = resolve; + stop = function () { + if (expectUnhandled === 0) { + resolve() + } else { + resolver = resolve; + } + } + }) + } + + before(() => { + unhandled = sinon.stub(); + window.addEventListener('unhandledrejection', unhandled); + }); + + after(() => { + window.removeEventListener('unhandledrejection', unhandled); + }); + + function getUnhandledErrors() { + return unhandled.args.map((args) => args[0].reason); + } + + Object.entries({ + 'simple reject': [1, (P) => { P.reject('err'); stop() }], + 'caught reject': [0, (P) => P.reject('err').catch((e) => { stop(); return e })], + 'unhandled reject with finally': [1, (P) => P.reject('err').finally(() => 'finally')], + 'error handler that throws': [1, (P) => P.reject('err').catch((e) => { stop(); throw e })], + 'rejection handled later in the chain': [0, (P) => P.reject('err').then((v) => v).catch((e) => { stop(); return e })], + 'multiple errors in one chain': [1, (P) => P.reject('err').then((v) => v).catch((e) => e).then((v) => { stop(); return P.reject(v) })], + 'multiple errors in one chain, all handled': [0, (P) => P.reject('err').then((v) => v).catch((e) => e).then((v) => P.reject(v)).catch((e) => { stop(); return e })], + 'separate chains for rejection and handling': [1, (P) => { + const p = P.reject('err'); + p.catch((e) => { stop(); return e; }) + p.then((v) => v); + }], + 'separate rejections merged without handling': [2, (P) => { + const p1 = P.reject('err1'); + const p2 = P.reject('err2'); + p1.then(() => p2).finally(stop); + }], + 'separate rejections merged for handling': [0, (P) => { + const p1 = P.reject('err1'); + const p2 = P.reject('err2'); + P.all([p1, p2]).catch((e) => { stop(); return e }); + }], + // eslint-disable-next-line no-throw-literal + 'exception in resolver': [1, (P) => new P(() => { stop(); throw 'err'; })], + // eslint-disable-next-line no-throw-literal + 'exception in resolver, caught': [0, (P) => new P(() => { throw 'err' }).catch((e) => { stop(); return e })], + 'errors from nested promises': [1, (P) => new P((resolve) => setTimeout(() => { resolve(P.reject('err')); stop(); }))], + 'errors from nested promises, caught': [0, (P) => new P((resolve) => setTimeout(() => resolve(P.reject('err')))).catch((e) => { stop(); return e })], + }).forEach(([t, [expectUnhandled, op]]) => { + describe(`on ${t}`, () => { + it('should match vanilla Promises', () => { + let vanillaUnhandled; + reset(expectUnhandled); + op(Promise); + return done.then(() => { + vanillaUnhandled = getUnhandledErrors(); + reset(expectUnhandled); + op(GreedyPromise); + return done; + }).then(() => { + const actualUnhandled = getUnhandledErrors(); + expect(actualUnhandled.length).to.eql(expectUnhandled); + expect(actualUnhandled).to.eql(vanillaUnhandled); + }) + }) + }) + }); + }); + + describe('idioms', () => { + let makePromise, pendingFailure, pendingSuccess; + + Object.entries({ + // eslint-disable-next-line no-throw-literal + 'resolver that throws': (P) => new P(() => { throw 'error' }), + 'resolver that resolves multiple times': (P) => new P((resolve) => { resolve('first'); resolve('second'); }), + 'resolver that rejects multiple times': (P) => new P((resolve, reject) => { reject('first'); reject('second') }), + 'resolver that resolves and rejects': (P) => new P((resolve, reject) => { reject('first'); resolve('second') }), + 'resolver that resolves with multiple arguments': (P) => new P((resolve) => resolve('one', 'two')), + 'resolver that rejects with multiple arguments': (P) => new P((resolve, reject) => reject('one', 'two')), + 'resolver that resolves to a promise': (P) => new P((resolve) => resolve(makePromise(P, 'val'))), + 'resolver that resolves to a promise that resolves to a promise': (P) => new P((resolve) => resolve(makePromise(P, makePromise(P, 'val')))), + 'resolver that resolves to a rejected promise': (P) => new P((resolve) => resolve(makePromise(P, 'err', true))), + 'simple .then': (P) => makePromise(P, 'value').then((v) => `${v} and then`), + 'chained .then': (P) => makePromise(P, 'value').then((v) => makePromise(P, `${v} and then`)), + '.then with error handler': (P) => makePromise(P, 'err', true).then(null, (e) => `${e} and then`), + '.then with chained error handler': (P) => makePromise(P, 'err', true).then(null, (e) => makePromise(P, `${e} and then`)), + '.then that throws': (P) => makePromise(P, 'value').then((v) => { throw v }), + '.then that throws in error handler': (P) => makePromise(P, 'err', true).then(null, (e) => { throw e }), + '.then with no args': (P) => makePromise(P, 'value').then(), + '.then that rejects': (P) => makePromise(P, 'value').then((v) => P.reject(v)), + '.then that rejects in error handler': (P) => makePromise(P, 'err', true).then(null, (err) => P.reject(err)), + '.then with no error handler on a rejection': (P) => makePromise(P, 'err', true).then((v) => `resolved ${v}`), + '.then with no success handler on a resolution': (P) => makePromise(P, 'value').then(null, (e) => `caught ${e}`), + 'simple .catch': (P) => makePromise(P, 'err', true).catch((err) => `caught ${err}`), + 'identity .catch': (P) => makePromise(P, 'err', true).catch((err) => err).then((v) => v), + '.catch that throws': (P) => makePromise(P, 'err', true).catch((err) => { throw err }), + 'chained .catch': (P) => makePromise(P, 'err', true).catch((err) => makePromise(P, err)), + 'chained .catch that rejects': (P) => makePromise(P, 'err', true).catch((err) => P.reject(`reject with ${err}`)), + 'simple .finally': (P) => { + let fval; + return makePromise(P, 'value') + .finally(() => fval = 'finally ran') + .then((val) => `${val} ${fval}`) + }, + 'chained .finally': (P) => { + let fval; + return makePromise(P, 'value') + .finally(() => pendingSuccess.then(() => { fval = 'finally ran' })) + .then((val) => `${val} ${fval}`) + }, + '.finally on a rejection': (P) => { + let fval; + return makePromise(P, 'error', true) + .finally(() => { fval = 'finally' }) + .catch((err) => `${err} ${fval}`) + }, + 'chained .finally on a rejection': (P) => { + let fval; + return makePromise(P, 'error', true) + .finally(() => pendingSuccess.then(() => { fval = 'finally' })) + .catch((err) => `${err} ${fval}`) + }, + // eslint-disable-next-line no-throw-literal + '.finally that throws': (P) => makePromise(P, 'value').finally(() => { throw 'error' }), + 'chained .finally that rejects': (P) => makePromise(P, 'value').finally(() => P.reject('error')), + 'scalar Promise.resolve': (P) => P.resolve('scalar'), + 'null Promise.resolve': (P) => P.resolve(null), + 'chained Promise.resolve': (P) => P.resolve(pendingSuccess), + 'chained Promise.resolve on failure': (P) => P.resolve(pendingFailure), + 'scalar Promise.reject': (P) => P.reject('scalar'), + 'chained Promise.reject': (P) => P.reject(pendingSuccess), + 'chained Promise.reject on failure': (P) => P.reject(pendingFailure), + 'simple Promise.all': (P) => P.all([makePromise(P, 'one'), makePromise(P, 'two')]), + 'Promise.all with scalars': (P) => P.all([makePromise(P, 'one'), 'two']), + 'Promise.all with errors': (P) => P.all([makePromise(P, 'one'), makePromise(P, 'two'), makePromise(P, 'err', true)]), + 'Promise.allSettled': (P) => P.allSettled([makePromise(P, 'one', true), makePromise(P, 'two'), makePromise(P, 'three', true)]), + 'Promise.allSettled with scalars': (P) => P.allSettled([makePromise(P, 'value'), 'scalar']), + 'Promise.race that succeeds': (P) => P.race([makePromise(P, 'error', true, 10), makePromise(P, 'success')]), + 'Promise.race that fails': (P) => P.race([makePromise(P, 'success', false, 10), makePromise(P, 'error', true)]), + 'Promise.race with scalars': (P) => P.race(['scalar', makePromise(P, 'success')]), + }).forEach(([t, op]) => { + describe(t, () => { + describe('when mixed with deferrals', () => { + beforeEach(() => { + makePromise = function(ctor, value, fail = false, delay = 0) { + // eslint-disable-next-line new-cap + return new ctor((resolve, reject) => { + setTimeout(() => fail ? reject(value) : resolve(value), delay) + }) + }; + pendingSuccess = makePromise(Promise, 'pending result', false, 10); + pendingFailure = makePromise(Promise, 'pending failure', true, 10); + }); + + it(`behaves like vanilla promises`, () => { + const vanilla = op(Promise); + const greedy = op(GreedyPromise); + // note that we are not using `allSettled` & co to resolve our promises, + // to avoid transformations those methods do under the hood + const {actual = {}, expected = {}} = {}; + return new Promise((resolve) => { + let pending = 2; + function collect(dest, slot) { + return function (value) { + dest[slot] = value; + pending--; + if (pending === 0) { + resolve() + } + } + } + vanilla.then(collect(expected, 'success'), collect(expected, 'failure')); + greedy.then(collect(actual, 'success'), collect(actual, 'failure')); + }).then(() => { + expect(actual).to.eql(expected); + }); + }); + + it(`once resolved, runs callbacks immediately`, () => { + const promise = op(GreedyPromise).catch(() => null); + return promise.then(() => { + let cbRan = false; + promise.then(() => { cbRan = true }); + expect(cbRan).to.be.true; + }); + }); + }); + + describe('when all promises involved are greedy', () => { + beforeEach(() => { + makePromise = function(ctor, value, fail = false, delay = 0) { + // eslint-disable-next-line new-cap + return new ctor((resolve, reject) => { + const run = () => fail ? reject(value) : resolve(value); + delay === 0 ? run() : setTimeout(run, delay); + }) + }; + pendingSuccess = makePromise(GreedyPromise, 'pending result'); + pendingFailure = makePromise(GreedyPromise, 'pending failure', true); + }); + + it('resolves immediately', () => { + let cbRan = false; + op(GreedyPromise).catch(() => null).then(() => { cbRan = true }); + expect(cbRan).to.be.true; + }); + }); + }); + }); + }); + + describe('.timeout', () => { + const timeout = GreedyPromise.timeout; + + it('should resolve immediately when ms is 0', () => { + let cbRan = false; + timeout(0.0).then(() => { cbRan = true }); + expect(cbRan).to.be.true; + }); + + it('should schedule timeout on ms > 0', (done) => { + let cbRan = false; + timeout(5).then(() => { cbRan = true }); + expect(cbRan).to.be.false; + setTimeout(() => { + expect(cbRan).to.be.true; + done(); + }, 10) + }); + }); +}); + +describe('promiseControls', () => { Object.entries({ 'resolve': (p) => p, 'reject': (p) => p.then(() => 'wrong', (v) => v) }).forEach(([method, transform]) => { describe(method, () => { - Object.entries({ - 'before the resolver': lazyControls, - 'after the resolver': promiseControls, - }).forEach(([t, controls]) => { - describe(`when called ${t}`, () => { - it(`should ${method} the promise`, () => { - const ctl = controls(); - ctl[method]('result'); - return transform(ctl.promise).then((res) => expect(res).to.equal('result')); - }); + it(`should ${method} the promise`, () => { + const ctl = defer(); + ctl[method]('result'); + return transform(ctl.promise).then((res) => expect(res).to.equal('result')); + }); - it('should ignore calls after the first', () => { - const ctl = controls(); - ctl[method]('result'); - ctl[method]('other'); - return transform(ctl.promise).then((res) => expect(res).to.equal('result')); - }); - }) - }) - }) + it('should ignore calls after the first', () => { + const ctl = defer(); + ctl[method]('result'); + ctl[method]('other'); + return transform(ctl.promise).then((res) => expect(res).to.equal('result')); + }); + }); }); }); From aa0fce8f8929ae93d7a5f4d49582c32bbba79d15 Mon Sep 17 00:00:00 2001 From: bretg Date: Wed, 20 Jul 2022 05:49:20 -0400 Subject: [PATCH 039/569] nextmillennium: update maintainer contact info (#8711) Got a group that can help with maintenance questions instead of an individual --- modules/nextMillenniumBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextMillenniumBidAdapter.md b/modules/nextMillenniumBidAdapter.md index 136f97d94d5..5374accfe35 100644 --- a/modules/nextMillenniumBidAdapter.md +++ b/modules/nextMillenniumBidAdapter.md @@ -2,7 +2,7 @@ ``` Module Name: NextMillennium Bid Adapter Module Type: Bidder Adapter -Maintainer: mihail.ivanchenko@nextmillennium.io +Maintainer: accountmanagers@nextmillennium.io ``` # Description From 8e945b40e1b3276d0be34b39293e84590ecbb57b Mon Sep 17 00:00:00 2001 From: JulieLorin Date: Wed, 20 Jul 2022 11:52:44 +0200 Subject: [PATCH 040/569] Big Richemedia Adapter : fix Creative size (#8713) --- modules/big-richmediaBidAdapter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/big-richmediaBidAdapter.js b/modules/big-richmediaBidAdapter.js index 2ee31e8cfd6..8a03aac1ace 100644 --- a/modules/big-richmediaBidAdapter.js +++ b/modules/big-richmediaBidAdapter.js @@ -8,7 +8,7 @@ const BIDDER_CODE = 'big-richmedia'; const metadataByRequestId = {}; export const spec = { - version: '1.5.0', + version: '1.5.1', code: BIDDER_CODE, gvlid: baseAdapter.GVLID, // use base adapter gvlid supportedMediaTypes: [ BANNER, VIDEO ], @@ -82,8 +82,8 @@ export const spec = { // This is a workaround needed for the rendering step (so that the adserver iframe does not get resized to 1800x1000 // when there is skin demand if (format === 'skin') { - renderParams.width = 1 - renderParams.height = 1 + bid.width = 1 + bid.height = 1 } const encoded = window.btoa(JSON.stringify(renderParams)); From cb7c5101ecf981aec178c0b17d6a1a1adb69e5f3 Mon Sep 17 00:00:00 2001 From: Anass Seddiki Date: Wed, 20 Jul 2022 17:29:09 +0200 Subject: [PATCH 041/569] Update 1plusX RTD Provider doc (#8714) --- modules/1plusXRtdProvider.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/modules/1plusXRtdProvider.md b/modules/1plusXRtdProvider.md index 75ad3b966d1..6a6211b37cc 100644 --- a/modules/1plusXRtdProvider.md +++ b/modules/1plusXRtdProvider.md @@ -8,9 +8,7 @@ ## Description -RTD provider for 1plusX. -Enriches the bidding object with Audience & Targeting data -Contact dc-team-1px@triplelift.com for information. +The 1plusX RTD module appends User and Contextual segments to the bidding object. ## Usage @@ -47,14 +45,14 @@ pbjs.setConfig({ ### Parameters -| Name | Type | Description | Notes | -| :---------------- | :------------ | :--------------------------------------------------------------- |:-------------------------------------------------------- | -| name | String | Real time data module name | Always '1plusX' | -| waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (optional) | `false` | -| params | Object | | | -| params.customerId | String | Your 1plusX customer id | | -| params.bidders | Array | List of bidders for which you would like data to be set | | -| params.timeout | Integer | timeout (ms) | 1000ms | +| Name | Type | Description | Default | +| :---------------- | :------------ | :--------------------------------------------------------------- |:----------------- | +| name | String | Real time data module name | Always '1plusX' | +| waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (optional) | `false` | +| params | Object | | | +| params.customerId | String | Your 1plusX customer id | | +| params.bidders | Array | List of bidders for which you would like data to be set | | +| params.timeout | Integer | timeout (ms) | 1000ms | ## Testing From 453ece28189c3851c07933e0e7eb17a653c7348b Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 20 Jul 2022 17:13:02 -0700 Subject: [PATCH 042/569] Rubicon Bid Adapter: Do not send storedrequests (#8717) * do NOT send stored requests! * set to undefined not delete faster? --- modules/rubiconBidAdapter.js | 5 ++++ test/spec/modules/rubiconBidAdapter_spec.js | 27 +++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index c551f8909cf..b08db65a6b6 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -320,6 +320,11 @@ export const spec = { // set ext.prebid.auctiontimestamp using auction time deepSetValue(data.imp[0], 'ext.prebid.auctiontimestamp', bidderRequest.auctionStart); + // set storedrequests to undefined so not sent to PBS + // top level and imp level both 'ext.prebid' objects are set above so no exception thrown here + data.ext.prebid.storedrequest = undefined; + data.imp[0].ext.prebid.storedrequest = undefined; + return { method: 'POST', url: `https://${rubiConf.videoHost || 'prebid-server'}.rubiconproject.com/openrtb2/auction`, diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index ebfe6689a3e..fb1b336ef92 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -2139,6 +2139,33 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].ext.data.pbadslot).to.equal('1234567890'); }); + it('should NOT include storedrequests in pbs payload', function () { + createVideoBidderRequest(); + bidderRequest.bids[0].ortb2 = { + ext: { + prebid: { + storedrequest: 'no-send-top-level-sr' + } + } + } + + bidderRequest.bids[0].ortb2Imp = { + ext: { + prebid: { + storedrequest: 'no-send-imp-sr' + } + } + } + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.ext.prebid.storedrequest).to.be.undefined; + expect(request.data.imp[0].ext.prebid.storedrequest).to.be.undefined; + }); + it('should include GAM ad unit in bid request', function () { createVideoBidderRequest(); bidderRequest.bids[0].ortb2Imp = { From d86712fd171b5af1c4da50513eb10955a6b1622f Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Thu, 21 Jul 2022 15:41:20 +0530 Subject: [PATCH 043/569] PBS Bid Adapter: Setting bidderCode and adapterCode for a bid (#8696) * Changed net revenue to True * Added bidderCode and adapterCode to bidObject * setting seat as default adapterCode * removed bidderCode * Added unit test cases * cloned the test object Co-authored-by: Azhar --- modules/prebidServerBidAdapter/index.js | 7 +++++++ .../modules/prebidServerBidAdapter_spec.js | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index dcc39c75715..4007c67c82f 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -984,6 +984,13 @@ Object.assign(ORTB2.prototype, { }); bidObject.requestTimestamp = this.requestTimestamp; bidObject.cpm = cpm; + if (bid?.ext?.prebid?.meta?.adaptercode) { + bidObject.adapterCode = bid.ext.prebid.meta.adaptercode; + } else if (bidRequest?.bidder) { + bidObject.adapterCode = bidRequest.bidder; + } else { + bidObject.adapterCode = seatbid.seat; + } // temporarily leaving attaching it to each bidResponse so no breaking change // BUT: this is a flat map, so it should be only attached to bidderRequest, a the change above does diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 6793bb988bf..1e81abda57e 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -2692,6 +2692,26 @@ describe('S2S Adapter', function () { sinon.assert.match(actual.imp[0], sinon.match(ortb2Imp)); }); + it('setting adapterCode for default bidder', function () { + config.setConfig({ CONFIG }); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB)); + + const response = addBidResponse.firstCall.args[1]; + expect(response).to.have.property('adapterCode', 'appnexus'); + }); + + it('setting adapterCode for alternate bidder', function () { + config.setConfig({ CONFIG }); + let RESPONSE_OPENRTB2 = deepClone(RESPONSE_OPENRTB); + RESPONSE_OPENRTB2.seatbid[0].bid[0].ext.prebid.meta.adaptercode = 'appnexus2' + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB2)); + + const response = addBidResponse.firstCall.args[1]; + expect(response).to.have.property('adapterCode', 'appnexus2'); + }); + describe('on sync requested with no cookie', () => { let cfg, req, csRes; From 9f9f58978ae5e0521b27fc382fb3ee0d50e10f20 Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Thu, 21 Jul 2022 12:22:15 +0200 Subject: [PATCH 044/569] onetag Bid Adapter : add support for price floors, supply chain, and gpid (#8675) * Adds support for price floor, supply chain, GPID * Removes unused import * Updates onetag test file * Remove trailing space * Add unit tests for schain validation function Co-authored-by: federico --- modules/onetagBidAdapter.js | 51 ++++++++++++++++--- test/spec/modules/onetagBidAdapter_spec.js | 57 ++++++++++++++++++++-- 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 89c614dba23..0364acd5d21 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -1,13 +1,13 @@ 'use strict'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {INSTREAM, OUTSTREAM} from '../src/video.js'; -import {Renderer} from '../src/Renderer.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; +import { Renderer } from '../src/Renderer.js'; import {find} from '../src/polyfill.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {createEidsArray} from './userId/eids.js'; -import {deepClone} from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { createEidsArray } from './userId/eids.js'; +import { deepClone, logError, deepAccess } from '../src/utils.js'; const ENDPOINT = 'https://onetag-sys.com/prebid-request'; const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; @@ -68,6 +68,9 @@ function buildRequests(validBidRequests, bidderRequest) { if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].userId) { payload.userId = createEidsArray(validBidRequests[0].userId); } + if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].schain && isSchainValid(validBidRequests[0].schain)) { + payload.schain = validBidRequests[0].schain; + } try { if (storage.hasLocalStorage()) { payload.onetagSid = storage.getDataFromLocalStorage('onetag_sid'); @@ -245,6 +248,7 @@ function requestsToBids(bidRequests) { // Other params videoObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.video); videoObj['type'] = VIDEO; + videoObj['priceFloors'] = getBidFloor(bidRequest, VIDEO, videoObj['playerSize']); return videoObj; }); const bannerBidRequests = bidRequests.filter(bidRequest => isValid(BANNER, bidRequest)).map(bidRequest => { @@ -253,6 +257,7 @@ function requestsToBids(bidRequests) { bannerObj['sizes'] = parseSizes(bidRequest); bannerObj['type'] = BANNER; bannerObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.banner); + bannerObj['priceFloors'] = getBidFloor(bidRequest, BANNER, bannerObj['sizes']); return bannerObj; }); return videoBidRequests.concat(bannerBidRequests); @@ -265,6 +270,7 @@ function setGeneralInfo(bidRequest) { this['bidderRequestId'] = bidRequest.bidderRequestId; this['auctionId'] = bidRequest.auctionId; this['transactionId'] = bidRequest.transactionId; + this['gpid'] = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); this['pubId'] = params.pubId; this['ext'] = params.ext; if (params.pubClick) { @@ -373,6 +379,37 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { return syncs; } +function getBidFloor(bidRequest, mediaType, sizes) { + const priceFloors = []; + if (typeof bidRequest.getFloor === 'function') { + sizes.forEach(size => { + const floor = bidRequest.getFloor({ + currency: 'EUR', + mediaType: mediaType || '*', + size: [size.width, size.height] + }); + floor.size = deepClone(size); + if (!floor.floor) { floor.floor = null; } + priceFloors.push(floor); + }); + } + return priceFloors; +} + +export function isSchainValid(schain) { + let isValid = false; + const requiredFields = ['asi', 'sid', 'hp']; + if (!schain || !schain.nodes) return isValid; + isValid = schain.nodes.reduce((status, node) => { + if (!status) return status; + return requiredFields.every(field => node.hasOwnProperty(field)); + }, true); + if (!isValid) { + logError('OneTag: required schain params missing'); + } + return isValid; +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index f335f2ec62a..2dc0a43bbb0 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -1,4 +1,4 @@ -import { spec, isValid, hasTypeVideo } from 'modules/onetagBidAdapter.js'; +import { spec, isValid, hasTypeVideo, isSchainValid } from 'modules/onetagBidAdapter.js'; import { expect } from 'chai'; import {find} from 'src/polyfill.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; @@ -15,7 +15,21 @@ describe('onetag', function () { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', - 'transactionId': 'qwerty123' + 'transactionId': 'qwerty123', + 'schain': { + 'validation': 'off', + 'config': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + } + ] + } + }, }; } @@ -193,7 +207,8 @@ describe('onetag', function () { 'context', 'playerSize', 'mediaTypeInfo', - 'type' + 'type', + 'priceFloors' ); } else if (isValid(BANNER, bid)) { expect(bid).to.have.all.keys( @@ -205,9 +220,13 @@ describe('onetag', function () { 'transactionId', 'mediaTypeInfo', 'sizes', - 'type' + 'type', + 'priceFloors' ); } + if (bid.schain && isSchainValid(bid.schain)) { + expect(data).to.have.all.keys('schain'); + } expect(bid.bidId).to.be.a('string'); expect(bid.pubId).to.be.a('string'); } @@ -358,6 +377,36 @@ describe('onetag', function () { expect(syncs[0].url).to.match(/(?:[?&](?:us_privacy=us_foo(?:[&][^&]*)*))+$/); }); }); + describe('isSchainValid', function () { + it('Should return false when schain is null or undefined', function () { + expect(isSchainValid(null)).to.be.false; + expect(isSchainValid(undefined)).to.be.false; + }); + it('Should return false when schain is missing nodes key', function () { + const schain = {'otherKey': 'otherValue'}; + expect(isSchainValid(schain)).to.be.false; + }); + it('Should return false when schain is missing one of the required SupplyChainNode attribute', function () { + const missingAsiNode = {'sid': '00001', 'hp': 1}; + const missingSidNode = {'asi': 'indirectseller.com', 'hp': 1}; + const missingHpNode = {'asi': 'indirectseller.com', 'sid': '00001'}; + expect(isSchainValid({'config': {'nodes': [missingAsiNode]}})).to.be.false; + expect(isSchainValid({'config': {'nodes': [missingSidNode]}})).to.be.false; + expect(isSchainValid({'config': {'nodes': [missingHpNode]}})).to.be.false; + }); + it('Should return true when schain contains all required attributes', function () { + const validSchain = { + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + } + ] + }; + expect(isSchainValid(validSchain)).to.be.true; + }) + }); }); function getBannerVideoResponse() { From f0596c4366fc3cb5b12b28ea44af8b54c9b6bbc9 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 21 Jul 2022 14:32:10 +0000 Subject: [PATCH 045/569] Prebid 7.7.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6b0a939a173..2f4ac5e0caf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.7.0-pre", + "version": "7.7.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 33dca05273f..e28267cd150 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.7.0-pre", + "version": "7.7.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From bef135eed895dadab0072a86afa291b7c1c6bc99 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 21 Jul 2022 14:32:10 +0000 Subject: [PATCH 046/569] Increment version to 7.8.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2f4ac5e0caf..19a4ed0770d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.7.0", + "version": "7.8.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index e28267cd150..5a4a3abdb10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.7.0", + "version": "7.8.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From c4c621ab8e3b531b9c4866cbf1e01abef8dcb4e4 Mon Sep 17 00:00:00 2001 From: vfourny <109095331+vfourny-ogury@users.noreply.github.com> Date: Thu, 21 Jul 2022 17:19:34 +0200 Subject: [PATCH 047/569] update ogury adapter (#8722) --- modules/oguryBidAdapter.js | 2 +- test/spec/modules/oguryBidAdapter_spec.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index 932d0d43f0e..e0630c7e412 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -11,7 +11,7 @@ const DEFAULT_TIMEOUT = 1000; const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; const TIMEOUT_MONITORING_HOST = 'https://ms-ads-monitoring-events.presage.io'; const MS_COOKIE_SYNC_DOMAIN = 'https://ms-cookie-sync.presage.io'; -const ADAPTER_VERSION = '1.2.12'; +const ADAPTER_VERSION = '1.2.13'; function getClientWidth() { const documentElementClientWidth = window.top.document.documentElement.clientWidth diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index 028c780d9fb..8871d2d0886 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -279,7 +279,7 @@ describe('OguryBidAdapter', function () { }, ext: { prebidversion: '$prebid.version$', - adapterversion: '1.2.12' + adapterversion: '1.2.13' }, device: { w: stubbedWidth, @@ -659,7 +659,7 @@ describe('OguryBidAdapter', function () { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain }, nurl: openRtbBidResponse.body.seatbid[0].bid[0].nurl, - adapterVersion: '1.2.12', + adapterVersion: '1.2.13', prebidVersion: '$prebid.version$' }, { requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, @@ -676,7 +676,7 @@ describe('OguryBidAdapter', function () { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain }, nurl: openRtbBidResponse.body.seatbid[0].bid[1].nurl, - adapterVersion: '1.2.12', + adapterVersion: '1.2.13', prebidVersion: '$prebid.version$' }] From 45b4dafc65555b4eca0a1251135f2f5d175179eb Mon Sep 17 00:00:00 2001 From: Michele Nasti Date: Thu, 21 Jul 2022 17:39:48 +0200 Subject: [PATCH 048/569] Rubicon Bid Adapter: remove fpd warning (#8688) * remove fpd warning * return undefined instead of skipping the if Co-authored-by: Michele Nasti --- modules/rubiconBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b08db65a6b6..192d5406b86 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1032,7 +1032,7 @@ function applyFPD(bidRequest, mediaType, data) { if (segments.length > 0) return segments.toString(); }).toString(); } else if (typeof prop === 'object' && !Array.isArray(prop)) { - logWarn('Rubicon: Filtered FPD key: ', key, ': Expected value to be string, integer, or an array of strings/ints'); + return undefined; } else if (typeof prop !== 'undefined') { return (Array.isArray(prop)) ? prop.filter(value => { if (typeof value !== 'object' && typeof value !== 'undefined') return value.toString(); From 16b74a7f275d12c9011e105b6cdb1243a6b13525 Mon Sep 17 00:00:00 2001 From: yieldlift <61510052+yieldlift@users.noreply.github.com> Date: Thu, 21 Jul 2022 17:47:01 +0200 Subject: [PATCH 049/569] Yieldlift Bid Adapter: improve adomain (#8708) * added meta.adomain * Changing adomain parameter to advertiserDomains * Merging master into branch Co-authored-by: Danijel Predarski --- modules/yieldliftBidAdapter.js | 4 +--- test/spec/modules/yieldliftBidAdapter_spec.js | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/yieldliftBidAdapter.js b/modules/yieldliftBidAdapter.js index 9fe1bfe84fc..fddc9a0d50b 100644 --- a/modules/yieldliftBidAdapter.js +++ b/modules/yieldliftBidAdapter.js @@ -96,9 +96,7 @@ export const spec = { creativeId: bid.crid, netRevenue: DEFAULT_NET_REVENUE, currency: DEFAULT_CURRENCY, - meta: { - adomain: bid.adomain - } + meta: { advertiserDomains: bid && bid.advertiserDomains ? bid.advertiserDomains : [] } }) }) } else { diff --git a/test/spec/modules/yieldliftBidAdapter_spec.js b/test/spec/modules/yieldliftBidAdapter_spec.js index 86a7b83e2c6..abb5a868c77 100644 --- a/test/spec/modules/yieldliftBidAdapter_spec.js +++ b/test/spec/modules/yieldliftBidAdapter_spec.js @@ -45,7 +45,7 @@ const RESPONSE = { 'price': 0.18, 'adm': '', 'adid': '144762342', - 'adomain': [ + 'advertiserDomains': [ 'https://dummydomain.com' ], 'iurl': 'iurl', @@ -74,7 +74,7 @@ const RESPONSE = { 'price': 0.1, 'adm': '', 'adid': '144762342', - 'adomain': [ + 'advertiserDomains': [ 'https://dummydomain.com' ], 'iurl': 'iurl', @@ -208,7 +208,7 @@ describe('YieldLift', function () { expect(bids[index]).to.have.property('height', RESPONSE.body.seatbid[0].bid[index].h); expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm); expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid); - expect(bids[index].meta).to.have.property('adomain', RESPONSE.body.seatbid[0].bid[index].adomain); + expect(bids[index].meta).to.have.property('advertiserDomains', RESPONSE.body.seatbid[0].bid[index].advertiserDomains); expect(bids[index]).to.have.property('ttl', 30); expect(bids[index]).to.have.property('netRevenue', true); } From 2684aa35ce97ab04279bbe426891ac6b95e84186 Mon Sep 17 00:00:00 2001 From: Gabriel Chicoye Date: Thu, 21 Jul 2022 19:31:48 +0200 Subject: [PATCH 050/569] Prisma Bid Adapter: initial bid adapter release (#8681) * prisma adapter added * bidResponses.meta.advertiserDomains update bidResponses.meta.advertiserDomains defaulted to [] * VideoExt removed from buildRequest Not necessary for now * Price floor removed * Floor and keyword tests removed --- modules/prismaBidAdapter.js | 199 ++++++++++++++++ modules/prismaBidAdapter.md | 59 +++++ test/spec/modules/prismaBidAdapter_spec.js | 261 +++++++++++++++++++++ 3 files changed, 519 insertions(+) create mode 100644 modules/prismaBidAdapter.js create mode 100644 modules/prismaBidAdapter.md create mode 100644 test/spec/modules/prismaBidAdapter_spec.js diff --git a/modules/prismaBidAdapter.js b/modules/prismaBidAdapter.js new file mode 100644 index 00000000000..52a8c18a6fd --- /dev/null +++ b/modules/prismaBidAdapter.js @@ -0,0 +1,199 @@ +import {ajax} from '../src/ajax.js'; +import {config} from '../src/config.js'; +import { transformBidderParamKeywords } from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'prisma'; +const BIDDER_URL = 'https://prisma.nexx360.io/prebid'; +const CACHE_URL = 'https://prisma.nexx360.io/cache'; +const METRICS_TRACKER_URL = 'https://prisma.nexx360.io/track-imp'; + +const GVLID = 965; + +function getConnectionType() { + const connection = navigator.connection || navigator.webkitConnection; + if (!connection) { + return 0; + } + switch (connection.type) { + case 'ethernet': + return 1; + case 'wifi': + return 2; + case 'cellular': + switch (connection.effectiveType) { + case 'slow-2g': + case '2g': + return 4; + case '3g': + return 5; + case '4g': + return 6; + default: + return 3; + } + default: + return 0; + } +} + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + aliases: ['prismadirect'], // short code + supportedMediaTypes: [BANNER, VIDEO], + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + return !!(bid.params.account && bid.params.tagId); + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + const adUnits = []; + const test = config.getConfig('debug') ? 1 : 0; + let adunitValue = null; + let userEids = null; + Object.keys(validBidRequests).forEach(key => { + adunitValue = validBidRequests[key]; + const foo = { + account: adunitValue.params.account, + tagId: adunitValue.params.tagId, + label: adunitValue.adUnitCode, + bidId: adunitValue.bidId, + auctionId: adunitValue.auctionId, + transactionId: adunitValue.transactionId, + mediatypes: adunitValue.mediaTypes, + bidfloor: 0, + bidfloorCurrency: 'USD', + keywords: adunitValue.params.keywords ? transformBidderParamKeywords(adunitValue.params.keywords) : [], + } + adUnits.push(foo); + if (adunitValue.userIdAsEids) userEids = adunitValue.userIdAsEids; + }); + const payload = { + adUnits, + // TODO: does the fallback make sense here? + href: encodeURIComponent(bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation) + }; + if (bidderRequest) { // modules informations (gdpr, ccpa, schain, userId) + if (bidderRequest.gdprConsent) { + payload.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + payload.gdprConsent = bidderRequest.gdprConsent.consentString; + } else { + payload.gdpr = 0; + payload.gdprConsent = ''; + } + if (bidderRequest.uspConsent) { payload.uspConsent = bidderRequest.uspConsent; } + if (bidderRequest.schain) { payload.schain = bidderRequest.schain; } + if (userEids !== null) payload.userEids = userEids; + }; + payload.connectionType = getConnectionType(); + + if (test) payload.test = 1; + const payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: BIDDER_URL, + data: payloadString, + }; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + const serverBody = serverResponse.body; + const bidResponses = []; + let bidResponse = null; + let value = null; + if (serverBody.hasOwnProperty('responses')) { + Object.keys(serverBody['responses']).forEach(key => { + value = serverBody['responses'][key]; + const url = `${CACHE_URL}?uuid=${value['uuid']}`; + bidResponse = { + requestId: value['bidId'], + cpm: value['cpm'], + currency: value['currency'], + width: value['width'], + height: value['height'], + ttl: value['ttl'], + creativeId: value['creativeId'], + netRevenue: true, + prisma: { + 'ssp': value['bidder'], + 'consent': value['consent'], + 'tagId': value['tagId'] + }, + meta: { + 'advertiserDomains': value['adomain'] || [] + } + }; + if (value.type === 'banner') bidResponse.adUrl = url; + if (value.type === 'video') { + const params = { + type: 'prebid', + mediatype: 'video', + ssp: value.bidder, + tag_id: value.tagId, + consent: value.consent, + price: value.cpm, + }; + bidResponse.cpm = value.cpm; + bidResponse.mediaType = 'video'; + bidResponse.vastUrl = url; + bidResponse.vastImpUrl = `${METRICS_TRACKER_URL}?${new URLSearchParams(params).toString()}`; + } + bidResponses.push(bidResponse); + }); + } + return bidResponses; + }, + + /** + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + if (typeof serverResponses === 'object' && serverResponses != null && serverResponses.length > 0 && serverResponses[0].hasOwnProperty('body') && + serverResponses[0].body.hasOwnProperty('cookies') && typeof serverResponses[0].body.cookies === 'object') { + return serverResponses[0].body.cookies.slice(0, 5); + } else { + return []; + } + }, + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} The bid that won the auction + */ + onBidWon: function(bid) { + // fires a pixel to confirm a winning bid + const params = { type: 'prebid', mediatype: 'banner' }; + if (bid.hasOwnProperty('prisma')) { + if (bid.prisma.hasOwnProperty('ssp')) params.ssp = bid.prisma.ssp; + if (bid.prisma.hasOwnProperty('tagId')) params.tag_id = bid.prisma.tagId; + if (bid.prisma.hasOwnProperty('consent')) params.consent = bid.prisma.consent; + }; + params.price = bid.cpm; + const url = `${METRICS_TRACKER_URL}?${new URLSearchParams(params).toString()}`; + ajax(url, null, undefined, {method: 'GET', withCredentials: true}); + return true; + } + +} +registerBidder(spec); diff --git a/modules/prismaBidAdapter.md b/modules/prismaBidAdapter.md new file mode 100644 index 00000000000..a400183cec6 --- /dev/null +++ b/modules/prismaBidAdapter.md @@ -0,0 +1,59 @@ +# Overview + +``` +Module Name: Prisma Bid Adapter +Module Type: Bidder Adapter +Maintainer: gabriel@nexx360.io +``` + +# Description + +Connects to Prisma network for bids. + +To use us as a bidder you must have an account and an active "tagId" on our platform. + +# Test Parameters + +## Web + +### Display +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'prisma', + params: { + account: '1067', + tagId: 'luvxjvgn' + } + }] + }, +]; +``` + +### Video Instream +``` + var videoAdUnit = { + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids: [{ + bidder: 'prisma', + params: { + account: '1067', + tagId: 'luvxjvgn' + } + }] + }; +``` diff --git a/test/spec/modules/prismaBidAdapter_spec.js b/test/spec/modules/prismaBidAdapter_spec.js new file mode 100644 index 00000000000..be1c16c9059 --- /dev/null +++ b/test/spec/modules/prismaBidAdapter_spec.js @@ -0,0 +1,261 @@ +import {expect} from 'chai'; +import {spec} from 'modules/prismaBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import {config} from 'src/config.js'; +import * as utils from 'src/utils.js'; +import { requestBidsHook } from 'modules/consentManagement.js'; + +describe('Prisma bid adapter tests', function () { + const DISPLAY_BID_REQUEST = [{ + 'bidder': 'prisma', + 'params': { + 'account': '1067', + 'tagId': 'luvxjvgn' + }, + 'userId': { + 'id5id': { + 'uid': 'ID5*hQ5WobYI9Od4u52qpaXVKHhxUa4DsOWRAlvaFajm8gINfI1oVAe3UK59416dT4TqDX1pj4MBJ5TYwir6x3JgBw1-avYHSnmvQDdRMbxmC2sNf3ggIRTbyQBdI1RjvHyeDYCsistnTXF_iKF1nutYeQ2BZ4P5d5muZTG7C2PXVFgNg-18io9dCiSjzJXx93KPDYRiuIwtsGGsp51rojlpFw2Fp_dUkjXl4CAblk58DvwNhobwQ27bnBP8F2-Pcs88DYcvKn4r6dm3Vi7ILttxDQ2IgZ2X44ClgjoWh-vRf6ANis8Z7uL16vO8q0P5C21eDYuc4v_KaZqN-p9YWEeEZQ2OpkbRL7n5NieVJExHM6ANkAlLZhVf2T-1906TAIHKDZFm_xMCa1jJfpBqZB2agw2TjfbK6wMtJeHiZaipSuUNlM_CSH0HVXtfMj9yfzjzDZZnltZQ9lvc4JhXye5AwA2X1f9Dhk8VURTvVdfEUlU', + 'ext': { + 'linkType': 2 + } + } + }, + 'userIdAsEids': [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': 'ID5*hQ5WobYI9Od4u52qpaXVKHhxUa4DsOWRAlvaFajm8gINfI1oVAe3UK59416dT4TqDX1pj4MBJ5TYwir6x3JgBw1-avYHSnmvQDdRMbxmC2sNf3ggIRTbyQBdI1RjvHyeDYCsistnTXF_iKF1nutYeQ2BZ4P5d5muZTG7C2PXVFgNg-18io9dCiSjzJXx93KPDYRiuIwtsGGsp51rojlpFw2Fp_dUkjXl4CAblk58DvwNhobwQ27bnBP8F2-Pcs88DYcvKn4r6dm3Vi7ILttxDQ2IgZ2X44ClgjoWh-vRf6ANis8Z7uL16vO8q0P5C21eDYuc4v_KaZqN-p9YWEeEZQ2OpkbRL7n5NieVJExHM6ANkAlLZhVf2T-1906TAIHKDZFm_xMCa1jJfpBqZB2agw2TjfbK6wMtJeHiZaipSuUNlM_CSH0HVXtfMj9yfzjzDZZnltZQ9lvc4JhXye5AwA2X1f9Dhk8VURTvVdfEUlU', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + } + ] + } + ], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, + 'adUnitCode': 'banner-div', + 'transactionId': '9ad89d90-eb73-41b9-bf5f-7a8e2eecff27', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4d9e29504f8af6', + 'bidderRequestId': '3423b6bd1a922c', + 'auctionId': '05e0a3a1-9f57-41f6-bbcb-2ba9c9e3d2d5', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }]; + + const DISPLAY_BID_RESPONSE = {'body': { + 'responses': [ + { + 'bidId': '4d9e29504f8af6', + 'cpm': 0.437245, + 'width': 300, + 'height': 250, + 'creativeId': '98493581', + 'currency': 'EUR', + 'netRevenue': true, + 'type': 'banner', + 'ttl': 360, + 'uuid': 'ce6d1ee3-2a05-4d7c-b97a-9e62097798ec', + 'bidder': 'appnexus', + 'consent': 1, + 'tagId': 'luvxjvgn' + } + ], + }}; + + const VIDEO_BID_REQUEST = [ + { + 'bidder': 'prisma', + 'params': { + 'account': '1067', + 'tagId': 'yqsc1tfj' + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [[640, 480]], + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5, 6], + 'playbackmethod': [2], + 'skip': 1 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '5434c81c-7210-44ae-9014-67c75dee48d0', + 'sizes': [[640, 480]], + 'bidId': '22f90541e576a3', + 'bidderRequestId': '1d4549243f3bfd', + 'auctionId': 'ed21b528-bcab-47e2-8605-ec9b71000c89', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ] + + const VIDEO_BID_RESPONSE = {'body': { + 'responses': [ + { + 'bidId': '2c129e8e01859a', + 'type': 'video', + 'uuid': 'b8e7b2f0-c378-479f-aa4f-4f55d5d7d1d5', + 'cpm': 4.5421, + 'width': 1, + 'height': 1, + 'creativeId': '97517771', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + 'bidder': 'appnexus', + 'consent': 1, + 'tagId': 'yqsc1tfj' + } + ] + }}; + + const DEFAULT_OPTIONS = { + gdprConsent: { + gdprApplies: true, + consentString: 'BOzZdA0OzZdA0AGABBENDJ-AAAAvh7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__79__3z3_9pxP78k89r7337Mw_v-_v-b7JCPN_Y3v-8Kg', + vendorData: {} + }, + refererInfo: { + referer: 'https://www.prebid.org', + canonicalUrl: 'https://www.prebid.org/the/link/to/the/page' + }, + uspConsent: '111222333', + userId: { 'id5id': { uid: '1111' } }, + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + }] + }, + }; + + it('Verify banner build request', function () { + const request = spec.buildRequests(DISPLAY_BID_REQUEST, DEFAULT_OPTIONS); + expect(request).to.have.property('url').and.to.equal('https://prisma.nexx360.io/prebid'); + expect(request).to.have.property('method').and.to.equal('POST'); + const requestContent = JSON.parse(request.data); + expect(requestContent.userEids.length).to.be.eql(1); + expect(requestContent.userEids[0]).to.have.property('source').and.to.equal('id5-sync.com'); + expect(requestContent.userEids[0]).to.have.property('uids'); + expect(requestContent.userEids[0].uids[0]).to.have.property('id').and.to.equal('ID5*hQ5WobYI9Od4u52qpaXVKHhxUa4DsOWRAlvaFajm8gINfI1oVAe3UK59416dT4TqDX1pj4MBJ5TYwir6x3JgBw1-avYHSnmvQDdRMbxmC2sNf3ggIRTbyQBdI1RjvHyeDYCsistnTXF_iKF1nutYeQ2BZ4P5d5muZTG7C2PXVFgNg-18io9dCiSjzJXx93KPDYRiuIwtsGGsp51rojlpFw2Fp_dUkjXl4CAblk58DvwNhobwQ27bnBP8F2-Pcs88DYcvKn4r6dm3Vi7ILttxDQ2IgZ2X44ClgjoWh-vRf6ANis8Z7uL16vO8q0P5C21eDYuc4v_KaZqN-p9YWEeEZQ2OpkbRL7n5NieVJExHM6ANkAlLZhVf2T-1906TAIHKDZFm_xMCa1jJfpBqZB2agw2TjfbK6wMtJeHiZaipSuUNlM_CSH0HVXtfMj9yfzjzDZZnltZQ9lvc4JhXye5AwA2X1f9Dhk8VURTvVdfEUlU'); + expect(requestContent.adUnits[0]).to.have.property('account').and.to.equal('1067'); + expect(requestContent.adUnits[0]).to.have.property('tagId').and.to.equal('luvxjvgn'); + expect(requestContent.adUnits[0]).to.have.property('label').and.to.equal('banner-div'); + expect(requestContent.adUnits[0]).to.have.property('bidId').and.to.equal('4d9e29504f8af6'); + expect(requestContent.adUnits[0]).to.have.property('auctionId').and.to.equal('05e0a3a1-9f57-41f6-bbcb-2ba9c9e3d2d5'); + expect(requestContent.adUnits[0]).to.have.property('mediatypes').exist; + expect(requestContent.adUnits[0].mediatypes).to.have.property('banner').exist; + expect(requestContent.adUnits[0]).to.have.property('bidfloor').and.to.equal(0); + expect(requestContent.adUnits[0]).to.have.property('bidfloorCurrency').and.to.equal('USD'); + expect(requestContent.adUnits[0]).to.have.property('keywords'); + expect(requestContent.adUnits[0].keywords.length).to.be.eql(0); + }); + + it('Verify banner parse response', function () { + const request = spec.buildRequests(DISPLAY_BID_REQUEST, DEFAULT_OPTIONS); + const response = spec.interpretResponse(DISPLAY_BID_RESPONSE, request); + expect(response).to.have.lengthOf(1); + const bid = response[0]; + expect(bid.cpm).to.equal(0.437245); + expect(bid.adUrl).to.equal('https://prisma.nexx360.io/cache?uuid=ce6d1ee3-2a05-4d7c-b97a-9e62097798ec'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('98493581'); + expect(bid.currency).to.equal('EUR'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(360); + expect(bid.requestId).to.equal('4d9e29504f8af6'); + expect(bid.prisma).to.exist; + expect(bid.prisma.ssp).to.equal('appnexus'); + }); + + it('Verify video build request', function () { + const request = spec.buildRequests(VIDEO_BID_REQUEST, DEFAULT_OPTIONS); + expect(request).to.have.property('url').and.to.equal('https://prisma.nexx360.io/prebid'); + expect(request).to.have.property('method').and.to.equal('POST'); + const requestContent = JSON.parse(request.data); + expect(requestContent.adUnits[0]).to.have.property('account').and.to.equal('1067'); + expect(requestContent.adUnits[0]).to.have.property('tagId').and.to.equal('yqsc1tfj'); + expect(requestContent.adUnits[0]).to.have.property('label').and.to.equal('video1'); + expect(requestContent.adUnits[0]).to.have.property('bidId').and.to.equal('22f90541e576a3'); + expect(requestContent.adUnits[0]).to.have.property('auctionId').and.to.equal('ed21b528-bcab-47e2-8605-ec9b71000c89'); + expect(requestContent.adUnits[0]).to.have.property('mediatypes').exist; + expect(requestContent.adUnits[0].mediatypes).to.have.property('video').exist; + }); + + it('Verify video parse response', function () { + const request = spec.buildRequests(VIDEO_BID_REQUEST, DEFAULT_OPTIONS); + const response = spec.interpretResponse(VIDEO_BID_RESPONSE, request); + expect(response).to.have.lengthOf(1); + const bid = response[0]; + expect(bid.cpm).to.equal(4.5421); + expect(bid.vastUrl).to.equal('https://prisma.nexx360.io/cache?uuid=b8e7b2f0-c378-479f-aa4f-4f55d5d7d1d5'); + expect(bid.vastImpUrl).to.equal('https://prisma.nexx360.io/track-imp?type=prebid&mediatype=video&ssp=appnexus&tag_id=yqsc1tfj&consent=1&price=4.5421'); + expect(bid.width).to.equal(1); + expect(bid.height).to.equal(1); + expect(bid.creativeId).to.equal('97517771'); + expect(bid.currency).to.equal('EUR'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(360); + expect(bid.requestId).to.equal('2c129e8e01859a'); + expect(bid.prisma).to.exist; + expect(bid.prisma.ssp).to.equal('appnexus'); + }); + + it('Verifies bidder code', function () { + expect(spec.code).to.equal('prisma'); + }); + + it('Verifies bidder aliases', function () { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.eql('prismadirect'); + }); + it('Verifies if bid request valid', function () { + expect(spec.isBidRequestValid(DISPLAY_BID_REQUEST[0])).to.equal(true); + }); + it('Verifies bid won', function () { + const request = spec.buildRequests(DISPLAY_BID_REQUEST, DEFAULT_OPTIONS); + const response = spec.interpretResponse(DISPLAY_BID_RESPONSE, request); + const won = spec.onBidWon(response[0]); + expect(won).to.equal(true); + }); + it('Verifies user sync without cookie in bid response', function () { + var syncs = spec.getUserSyncs({}, [DISPLAY_BID_RESPONSE], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.lengthOf(0); + }); + it('Verifies user sync with cookies in bid response', function () { + DISPLAY_BID_RESPONSE.body.cookies = [{'type': 'image', 'url': 'http://www.cookie.sync.org/'}]; + var syncs = spec.getUserSyncs({}, [DISPLAY_BID_RESPONSE], DEFAULT_OPTIONS.gdprConsent); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0]).to.have.property('type').and.to.equal('image'); + expect(syncs[0]).to.have.property('url').and.to.equal('http://www.cookie.sync.org/'); + }); + it('Verifies user sync with no bid response', function() { + var syncs = spec.getUserSyncs({}, null, DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.lengthOf(0); + }); + it('Verifies user sync with no bid body response', function() { + var syncs = spec.getUserSyncs({}, [], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.lengthOf(0); + var syncs = spec.getUserSyncs({}, [{}], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.lengthOf(0); + }); +}); From f39c698b57a0df7fbd8f12524b46884cac9c7051 Mon Sep 17 00:00:00 2001 From: Kenan Gillet <1706856+kenan-gillet@users.noreply.github.com> Date: Thu, 21 Jul 2022 11:41:23 -0700 Subject: [PATCH 051/569] openxOrtbBidAdapter: avoid error logging when response body is null (#8725) ``` prebid.js:5 Prebid openx ERROR: Bidder openx failed to interpret the server's response. Continuing without bids null TypeError: Cannot use 'in' operator to search for 'nbr' in at prebid.js:12:4571 ``` Thanks @piotrj-rtbh --- modules/openxOrtbBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openxOrtbBidAdapter.js b/modules/openxOrtbBidAdapter.js index 1885f5122eb..a7a8d53a624 100644 --- a/modules/openxOrtbBidAdapter.js +++ b/modules/openxOrtbBidAdapter.js @@ -263,7 +263,7 @@ function interpretResponse(resp, req) { } const respBody = resp.body; - if ('nbr' in respBody) { + if (!respBody || 'nbr' in respBody) { return []; } From f7196dc30f62a15825bc811c0d17f344b5be7750 Mon Sep 17 00:00:00 2001 From: Jason Lydon <95770514+ftxmoJason@users.noreply.github.com> Date: Thu, 21 Jul 2022 14:41:44 -0400 Subject: [PATCH 052/569] Ftrack Id Module: adding parameter to callback in getId return object (#8678) * Adding cb to the callback * Delete package-lock.json Removing package-lock.json because it has conflicts and its not on ftrack to change it either way * Un conflicting the package-lock * ftrack: getting data to return in the EIDS * ftrack: getting data to return in the EIDS lint * ftrack: making the eids return all IDs * ftrack: making the eids return all IDs * ftrack: tweaking the EID schema per MediaGrid's requests * Added tests for getUserIdsAsEids method * Updated the ftrack decode function with suggested code and updated related tests * Updated getUserIdsAsEids tests to match value directly Co-authored-by: Jason Lydon Co-authored-by: Jared --- modules/ftrackIdSystem.js | 21 +++- modules/ftrackIdSystem.md | 16 ++- modules/userId/eids.js | 23 +++- modules/userId/eids.md | 2 +- test/spec/modules/ftrackIdSystem_spec.js | 131 +++++++++++++++++++++-- 5 files changed, 173 insertions(+), 20 deletions(-) diff --git a/modules/ftrackIdSystem.js b/modules/ftrackIdSystem.js index 17e210d5358..55f04e6b1d3 100644 --- a/modules/ftrackIdSystem.js +++ b/modules/ftrackIdSystem.js @@ -48,9 +48,20 @@ export const ftrackIdSubmodule = { * similar to the module name and ending in id or Id */ decode (value, config) { + if (!value) { return } + const ext = {} + + for (var key in value) { + /** unpack the strings from the arrays */ + ext[key] = value[key][0] + } + return { - ftrackId: value - }; + ftrackId: { + uid: value.DeviceID && value.DeviceID[0], + ext + } + } }, /** @@ -60,13 +71,13 @@ export const ftrackIdSubmodule = { * @param {SubmoduleConfig} config * @param {ConsentData} consentData * @param {(Object|undefined)} cacheIdObj - * @returns {IdResponse|undefined} + * @returns {IdResponse|undefined} A response object that contains id and/or callback. */ getId (config, consentData, cacheIdObj) { if (this.isConfigOk(config) === false || this.isThereConsent(consentData) === false) return undefined; return { - callback: function () { + callback: function (cb) { window.D9v = { UserID: '99999999999999', CampID: '3175', @@ -82,6 +93,8 @@ export const ftrackIdSubmodule = { storage.setDataInLocalStorage(`${FTRACK_PRIVACY_STORAGE_NAME}`, JSON.stringify(consentInfo)); }; + if (typeof cb === 'function') cb(response); + return response; } }; diff --git a/modules/ftrackIdSystem.md b/modules/ftrackIdSystem.md index 0c92f5afab1..24a8dbd08b6 100644 --- a/modules/ftrackIdSystem.md +++ b/modules/ftrackIdSystem.md @@ -80,4 +80,18 @@ You may request by emailing [mailto:privacy@flashtalking.com](privacy@flashtalki #### GDPR -In its current state, Flashtalking’s FTrack Identity Framework User ID Module does not create an ID if a user's consentData is "truthy" (true, 1). In other words, if GDPR applies in any way to a user, FTrack does not create an ID. \ No newline at end of file +In its current state, Flashtalking’s FTrack Identity Framework User ID Module does not create an ID if a user's consentData is "truthy" (true, 1). In other words, if GDPR applies in any way to a user, FTrack does not create an ID. + +--- + +### If you are using pbjs.getUserIdsAsEids(): + +Please note that the `uids` value is a stringified object of the IDs so publishers will need to `JSON.parse()` the value in order to use it: + +``` +{ + "HHID": [""], + "DeviceID": [""], + "SingleDeviceID": ["USERS SINGLE DEVICE ID"] +} +``` \ No newline at end of file diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 2649292b895..4b5fd55aa21 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -68,12 +68,14 @@ export const USER_IDS_CONFIG = { source: 'flashtalking.com', atype: 1, getValue: function(data) { - return data.uid + let value = ''; + if (data.DeviceID) { + value = data.DeviceID.join(','); + } + return value; }, getUidExt: function(data) { - if (data.ext) { - return data.ext; - } + return 'DeviceID'; } }, @@ -332,7 +334,6 @@ function createEidObject(userIdData, subModuleKey) { if (conf && userIdData) { let eid = {}; eid.source = isFn(conf['getSource']) ? conf['getSource'](userIdData) : conf['source']; - const value = isFn(conf['getValue']) ? conf['getValue'](userIdData) : userIdData; if (isStr(value)) { const uid = { id: value, atype: conf['atype'] }; @@ -367,6 +368,18 @@ export function createEidsArray(bidRequestUserId) { if (bidRequestUserId.hasOwnProperty(subModuleKey)) { if (subModuleKey === 'pubProvidedId') { eids = eids.concat(bidRequestUserId['pubProvidedId']); + } else if (subModuleKey === 'ftrackId') { + // ftrack has multiple IDs so we add each one that exists + let eid = { + 'atype': 1, + 'id': (bidRequestUserId[subModuleKey]['DeviceID'] || []).join('|'), + 'ext': {} + } + for (let id in bidRequestUserId[subModuleKey]) { + eid.ext[id] = (bidRequestUserId[subModuleKey][id] || []).join('|'); + } + + eids.push(eid); } else if (Array.isArray(bidRequestUserId[subModuleKey])) { bidRequestUserId[subModuleKey].forEach((config, index, arr) => { const eid = createEidObject(config, subModuleKey); diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 2372bb4b6fd..83414cbe295 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -75,7 +75,7 @@ userIdAsEids = [ { source: 'flashtalking.com', uids: [{ - id: 'some-random-id-value', + id: 'the-ids-object-stringified', atype: 1 }, diff --git a/test/spec/modules/ftrackIdSystem_spec.js b/test/spec/modules/ftrackIdSystem_spec.js index f2b0456a849..333a8cce624 100644 --- a/test/spec/modules/ftrackIdSystem_spec.js +++ b/test/spec/modules/ftrackIdSystem_spec.js @@ -2,6 +2,10 @@ import { ftrackIdSubmodule } from 'modules/ftrackIdSystem.js'; import * as utils from 'src/utils.js'; import { uspDataHandler } from 'src/adapterManager.js'; import { loadExternalScript } from 'src/adloader.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { init, setSubmoduleRegistry } from 'modules/userId/index.js'; +import {createEidsArray} from 'modules/userId/eids.js'; +import {config} from 'src/config.js'; let expect = require('chai').expect; let server; @@ -147,7 +151,7 @@ describe('FTRACK ID System', () => { }); it(`should be the only method that gets a new ID aka hits the D9 endpoint`, () => { - ftrackIdSubmodule.getId(configMock, null, null).callback(); + ftrackIdSubmodule.getId(configMock, null, null).callback(() => {}); expect(loadExternalScript.called).to.be.ok; expect(loadExternalScript.args[0][0]).to.deep.equal('https://d9.flashtalking.com/d9core'); loadExternalScript.resetHistory(); @@ -168,7 +172,7 @@ describe('FTRACK ID System', () => { it(`should use default IDs if config.params.id is not populated`, () => { let configMock1 = JSON.parse(JSON.stringify(configMock)); delete configMock1.params.ids; - ftrackIdSubmodule.getId(configMock1, null, null).callback(); + ftrackIdSubmodule.getId(configMock1, null, null).callback(() => {}); expect(window.D9r).to.have.property('DeviceID', true); expect(window.D9r).to.have.property('SingleDeviceID', true); @@ -181,7 +185,7 @@ describe('FTRACK ID System', () => { configMock1.params.ids['device id'] = 'test device ID'; configMock1.params.ids['single device id'] = 'test single device ID'; configMock1.params.ids['household id'] = 'test household ID'; - ftrackIdSubmodule.getId(configMock1, null, null).callback(); + ftrackIdSubmodule.getId(configMock1, null, null).callback(() => {}); expect(window.D9r).to.not.have.property('DeviceID'); expect(window.D9r).to.not.have.property('SingleDeviceID'); @@ -193,7 +197,7 @@ describe('FTRACK ID System', () => { configMock1.params.ids['device id'] = false; configMock1.params.ids['single device id'] = false; configMock1.params.ids['household id'] = false; - ftrackIdSubmodule.getId(configMock1, null, null).callback(); + ftrackIdSubmodule.getId(configMock1, null, null).callback(() => {}); expect(window.D9r).to.not.have.property('DeviceID'); expect(window.D9r).to.not.have.property('SingleDeviceID'); @@ -203,7 +207,7 @@ describe('FTRACK ID System', () => { it(`- only device id`, () => { let configMock1 = JSON.parse(JSON.stringify(configMock)); delete configMock1.params.ids['single device id']; - ftrackIdSubmodule.getId(configMock1, null, null).callback(); + ftrackIdSubmodule.getId(configMock1, null, null).callback(() => {}); expect(window.D9r).to.have.property('DeviceID', true); expect(window.D9r).to.not.have.property('SingleDeviceID'); @@ -213,7 +217,7 @@ describe('FTRACK ID System', () => { it(`- only single device id`, () => { let configMock1 = JSON.parse(JSON.stringify(configMock)); delete configMock1.params.ids['device id']; - ftrackIdSubmodule.getId(configMock1, null, null).callback(); + ftrackIdSubmodule.getId(configMock1, null, null).callback(() => {}); expect(window.D9r).to.not.have.property('DeviceID'); expect(window.D9r).to.have.property('SingleDeviceID', true); @@ -225,7 +229,7 @@ describe('FTRACK ID System', () => { delete configMock1.params.ids['device id']; delete configMock1.params.ids['single device id']; configMock1.params.ids['household id'] = true; - ftrackIdSubmodule.getId(configMock1, null, null).callback(); + ftrackIdSubmodule.getId(configMock1, null, null).callback(() => {}); expect(window.D9r).to.not.have.property('DeviceID'); expect(window.D9r).to.not.have.property('SingleDeviceID'); @@ -243,7 +247,7 @@ describe('FTRACK ID System', () => { expect(window.localStorage.getItem('ftrack-rtd')).to.not.be.ok; expect(window.localStorage.getItem('ftrack-rtd_exp')).to.not.be.ok; - ftrackIdSubmodule.getId(configMock, consentDataMock, null).callback(); + ftrackIdSubmodule.getId(configMock, consentDataMock, null).callback(() => {}); return new Promise(function(resolve, reject) { window.testTimer = function () { // Sinon fake server is NOT changing the readyState to 4, so instead @@ -271,7 +275,12 @@ describe('FTRACK ID System', () => { describe(`decode() method`, () => { it(`should respond with an object with the key 'ftrackId'`, () => { - expect(ftrackIdSubmodule.decode('value', configMock)).to.deep.equal({ftrackId: 'value'}); + expect(ftrackIdSubmodule.decode('value', configMock)).to.deep.equal({ + ftrackId: { + ext: { 0: 'v', 1: 'a', 2: 'l', 3: 'u', 4: 'e' }, + uid: undefined, + }, + }); }); it(`should not be making requests to retrieve a new ID, it should just be decoding a response`, () => { @@ -298,4 +307,108 @@ describe('FTRACK ID System', () => { expect(ftrackIdSubmodule.extendId(configMock, null, {cache: {id: ''}})).to.deep.equal({cache: {id: ''}}); }); }); + + describe('Uses ftrack getUserIdsAsEids() method', () => { + it('getUserIdsAsEids using the ftrack submodule and gets three ids (HHID, DeviceId, SingleDeviceId)', () => { + init(config); + setSubmoduleRegistry([ftrackIdSubmodule]); + + const ids = { + ftrackId: { + HHID: ['household_test_id'], + DeviceID: ['device_test_id'], + SingleDeviceID: ['single_device_test_id'] + } + }; + config.setConfig({ + userSync: { + auctionDelay: 10, + userIds: [{ + name: 'ftrack', value: ids, + }] + } + }); + + getGlobal().getUserIdsAsync().then((ids) => { + expect(getGlobal().getUserIdsAsEids()).to.deep.equal([{ + id: 'device_test_id', + atype: 1, + ext: { + HHID: 'household_test_id', + DeviceID: 'device_test_id', + SingleDeviceID: 'single_device_test_id', + } + }]); + }); + }); + + it('gets only the deviceId', () => { + init(config); + setSubmoduleRegistry([ftrackIdSubmodule]); + + const ids = { ftrackId: { DeviceID: ['device_test_id'] } }; + config.setConfig({ + userSync: { + auctionDelay: 10, + userIds: [{ + name: 'ftrack', value: ids, + }] + } + }); + + getGlobal().getUserIdsAsync().then((ids) => { + expect(getGlobal().getUserIdsAsEids()).to.deep.equal([{ + id: 'device_test_id', + atype: 1, + ext: { DeviceID: 'device_test_id' } + }]); + }); + }); + + it('gets only the user household id', () => { + init(config); + setSubmoduleRegistry([ftrackIdSubmodule]); + + const ids = { ftrackId: { HHID: ['household_test_id'], } }; + config.setConfig({ + userSync: { + auctionDelay: 10, + userIds: [{ + name: 'ftrack', value: ids, + }] + } + }); + + getGlobal().getUserIdsAsync().then((ids) => { + expect(getGlobal().getUserIdsAsEids()).to.deep.equal([{ + id: '', + atype: 1, + ext: { HHID: 'household_test_id' } + }]); + }); + }); + + it('gets only the deviceId', () => { + init(config); + setSubmoduleRegistry([ftrackIdSubmodule]); + + const ids = { ftrackId: { SingleDeviceID: ['single_device_test_id'] } }; + config.setConfig({ + userSync: { + auctionDelay: 10, + userIds: [{ + name: 'ftrack', value: ids, + }] + } + }); + + getGlobal().getUserIdsAsync().then((ids) => { + expect(getGlobal().getUserIdsAsEids()).to.deep.equal([{ + id: '', + atype: 1, + ext: { SingleDeviceID: 'single_device_test_id' } + }]); + }); + }); + }); }); From b5479d9acf73705753ffaa0c506e9b05e06e47d5 Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Thu, 21 Jul 2022 20:42:59 +0200 Subject: [PATCH 053/569] Smartx Bid Adapter: update to newest smartclip outstream player version 6 (#8721) * Add smartclipBidAdapter * smartxBidAdapter.js - removed unused variables, removed debug, added window before the outstream related functions * - made outstream player configurable * remove wrong named files * camelcase * fix * Out-Stream render update to SmartPlay 5.2 * ESlint fix * ESlint fix * ESlint fix * adjust tests, fixes * ESlint * adjusted desired bitrate examples * added bid.meta.advertiserDomains support * bug fix for numeric elementID outstream render * fix renderer url * support for floors module * bugfixes to be openRTB 2.5 compliant * update internal renderer usage * remove unused outstream_function logic * bugfix outstream options for default outstream renderer configuration * [PREB-10] fix empty title not configurable * add pbjs version * testing with outstream 5.3.0 * pbjs version into content.ext * made visibilityThreshold configurable * adjust position of pbjs version * Merge branch 'master' of https://github.com/prebid/Prebid.js into HEAD * update smartclip outstream player version to support outstream 6 release along with necessary config changes Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini --- modules/smartxBidAdapter.js | 58 ++++++++++++---------- test/spec/modules/smartxBidAdapter_spec.js | 6 +-- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 5b36e17be11..f3188c1f110 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -303,7 +303,7 @@ export const spec = { const playersize = deepAccess(currentBidRequest, 'mediaTypes.video.playerSize'); const renderer = Renderer.install({ id: 0, - url: 'https://dco.smartclip.net/?plc=7777778', + url: 'https://dco.smartclip.net/?plc=7777779', config: { adText: 'SmartX Outstream Video Ad via Prebid.js', player_width: playersize[0][0], @@ -353,65 +353,73 @@ function createOutstreamConfig(bid) { logMessage('[SMARTX][renderer] Handle SmartX outstream renderer'); - var smartPlayObj = { + var playerConfig = { minAdWidth: confMinAdWidth, maxAdWidth: confMaxAdWidth, - onStartCallback: function (m, n) { + coreSetup: {}, + layoutSettings: {}, + onCappedCallback: function() { try { - window.sc_smartIntxtStart(n); - } catch (f) {} - }, - onCappedCallback: function (m, n) { - try { - window.sc_smartIntxtNoad(n); - } catch (f) {} - }, - onEndCallback: function (m, n) { - try { - window.sc_smartIntxtEnd(n); + window.sc_smartIntxtNoad(); } catch (f) {} }, }; if (confStartOpen == 'true') { - smartPlayObj.startOpen = true; + playerConfig.startOpen = true; } else if (confStartOpen == 'false') { - smartPlayObj.startOpen = false; + playerConfig.startOpen = false; } if (confEndingScreen == 'true') { - smartPlayObj.endingScreen = true; + playerConfig.endingScreen = true; } else if (confEndingScreen == 'false') { - smartPlayObj.endingScreen = false; + playerConfig.endingScreen = false; } if (confTitle || (typeof bid.renderer.config.outstream_options.title == 'string' && bid.renderer.config.outstream_options.title == '')) { - smartPlayObj.title = confTitle; + playerConfig.layoutSettings.advertisingLabel = confTitle; } if (confSkipOffset) { - smartPlayObj.skipOffset = confSkipOffset; + playerConfig.coreSetup.skipOffset = confSkipOffset; } if (confDesiredBitrate) { - smartPlayObj.desiredBitrate = confDesiredBitrate; + playerConfig.coreSetup.desiredBitrate = confDesiredBitrate; } if (confVisibilityThreshold) { - smartPlayObj.visibilityThreshold = confVisibilityThreshold; + playerConfig.visibilityThreshold = confVisibilityThreshold; } - smartPlayObj.adResponse = bid.vastContent; + playerConfig.adResponse = bid.vastContent; const divID = '[id="' + elementId + '"]'; + var playerListener = function callback(event) { + switch (event) { + case 'AdSlotStarted': + try { + window.sc_smartIntxtStart(); + } catch (f) {} + break; + + case 'AdSlotComplete': + try { + window.sc_smartIntxtEnd(); + } catch (f) {} + break; + } + }; + try { // eslint-disable-next-line - let _outstreamPlayer = new OutstreamPlayer(divID, smartPlayObj); + outstreamplayer.connect(divID).setup(playerConfig, playerListener) } catch (e) { logError('[SMARTX][renderer] Error caught: ' + e); } - return smartPlayObj; + return playerConfig; } /** diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index b7235eee878..58ce50efb8e 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -514,7 +514,7 @@ describe('The smartx adapter', function () { responses[0].renderer.render(responses[0]); - expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777778'); + expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777779'); window.document.getElementById.restore(); }); @@ -542,7 +542,7 @@ describe('The smartx adapter', function () { responses[0].renderer.render(responses[0]); - expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777778'); + expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777779'); window.document.getElementById.restore(); }); @@ -560,7 +560,7 @@ describe('The smartx adapter', function () { responses[0].renderer.render(responses[0]); - expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777778'); + expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777779'); window.document.getElementById.restore(); }); From a838c8f2f9b7d3ec3965439bc8f7094ab37a1ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20Kok=C5=82owski?= <38455696+skoklowski@users.noreply.github.com> Date: Thu, 21 Jul 2022 20:43:33 +0200 Subject: [PATCH 054/569] rasBidAdapter - enpoint bugfix (#8726) --- modules/rasBidAdapter.js | 4 +++- test/spec/modules/rasBidAdapter_spec.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/rasBidAdapter.js b/modules/rasBidAdapter.js index d9e6b6a9126..909b6a7b795 100644 --- a/modules/rasBidAdapter.js +++ b/modules/rasBidAdapter.js @@ -5,7 +5,9 @@ import { isEmpty, getAdUnitSizes, parseSizesInput, deepAccess } from '../src/uti const BIDDER_CODE = 'ras'; const VERSION = '1.0'; -const getEndpoint = (network) => `https://csr.onet.pl/${encodeURIComponent(network)}/csr-006/csr.json?`; +const getEndpoint = (network) => { + return `https://csr.onet.pl/${encodeURIComponent(network)}/csr-006/csr.json?nid=${encodeURIComponent(network)}&`; +}; function parseParams(params, bidderRequest) { const newParams = {}; diff --git a/test/spec/modules/rasBidAdapter_spec.js b/test/spec/modules/rasBidAdapter_spec.js index e3f41566c99..324bf782672 100644 --- a/test/spec/modules/rasBidAdapter_spec.js +++ b/test/spec/modules/rasBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { spec } from 'modules/rasBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; -const CSR_ENDPOINT = 'https://csr.onet.pl/4178463/csr-006/csr.json?'; +const CSR_ENDPOINT = 'https://csr.onet.pl/4178463/csr-006/csr.json?nid=4178463&'; describe('rasBidAdapter', function () { const adapter = newBidder(spec); From 213ea7192793bbcf0a25c036e36e6e5e83436557 Mon Sep 17 00:00:00 2001 From: haruka-yamashita2 <39541428+haruka-yamashita2@users.noreply.github.com> Date: Fri, 22 Jul 2022 03:44:17 +0900 Subject: [PATCH 055/569] support for ID5 (#8712) --- modules/yieldoneBidAdapter.js | 6 ++++ test/spec/modules/yieldoneBidAdapter_spec.js | 33 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index 95c0a5b787a..98a95b6758e 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -80,6 +80,12 @@ export const spec = { payload.fuuid = dacId; } + // ID5 + const id5id = deepAccess(bidRequest, 'userId.id5id.uid'); + if (isStr(id5id) && !isEmpty(id5id)) { + payload.id5Id = id5id; + } + return { method: 'GET', url: ENDPOINT_URL, diff --git a/test/spec/modules/yieldoneBidAdapter_spec.js b/test/spec/modules/yieldoneBidAdapter_spec.js index eef25649a02..e41764b2876 100644 --- a/test/spec/modules/yieldoneBidAdapter_spec.js +++ b/test/spec/modules/yieldoneBidAdapter_spec.js @@ -436,6 +436,39 @@ describe('yieldoneBidAdapter', function() { expect(request[0].data.fuuid).to.equal('dacId_sample'); }); }); + + describe('ID5', function () { + it('dont send ID5 if undefined', function () { + const bidRequests = [ + { + params: {placementId: '0'}, + }, + { + params: {placementId: '1'}, + userId: {}, + }, + { + params: {placementId: '2'}, + userId: undefined, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data).to.not.have.property('id5Id'); + expect(request[1].data).to.not.have.property('id5Id'); + expect(request[2].data).to.not.have.property('id5Id'); + }); + + it('should send ID5 if available', function () { + const bidRequests = [ + { + params: {placementId: '0'}, + userId: {id5id: {uid: 'id5id_sample'}}, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data.id5Id).to.equal('id5id_sample'); + }); + }); }); describe('interpretResponse', function () { From 6536b3525016548b6af4f55cdb4d1978766b312c Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 22 Jul 2022 13:00:42 -0400 Subject: [PATCH 056/569] Various adapters: remove use of storageManager bypasses (#8690) * Update sspBCBidAdapter.js * Update fintezaAnalyticsAdapter.js * Update widespaceBidAdapter.js * Update kargoBidAdapter.js * Update fintezaAnalyticsAdapter.js * Update kargoBidAdapter.js * Update kargoBidAdapter.js * Update kargoBidAdapter.js * Update kargoBidAdapter_spec.js * Update kargoBidAdapter.js * Update sspBCBidAdapter.js * Update sspBCBidAdapter.js * Update kargoBidAdapter.js * Update pubwiseAnalyticsAdapter.js * Update pubwiseAnalyticsAdapter.js * Update invibesBidAdapter.js * Update insticatorBidAdapter.js * Update pubwiseAnalyticsAdapter.js * Update pubwiseAnalyticsAdapter.js * Update insticatorBidAdapter.js * Update insticatorBidAdapter_spec.js * Update invibesBidAdapter.js * Update insticatorBidAdapter_spec.js * Update widespaceBidAdapter.js * Update kargoBidAdapter.js * Kargo: URL-decoding test strings (#8727) Co-authored-by: Jeremy Sadwith --- modules/fintezaAnalyticsAdapter.js | 10 +------ modules/insticatorBidAdapter.js | 4 +-- modules/invibesBidAdapter.js | 2 +- modules/kargoBidAdapter.js | 28 ++----------------- modules/pubwiseAnalyticsAdapter.js | 2 +- modules/sspBCBidAdapter.js | 6 ++-- modules/widespaceBidAdapter.js | 22 --------------- .../spec/modules/insticatorBidAdapter_spec.js | 7 ++++- test/spec/modules/kargoBidAdapter_spec.js | 15 ++++++---- 9 files changed, 27 insertions(+), 69 deletions(-) diff --git a/modules/fintezaAnalyticsAdapter.js b/modules/fintezaAnalyticsAdapter.js index 7ecc7e963b5..10b59de1838 100644 --- a/modules/fintezaAnalyticsAdapter.js +++ b/modules/fintezaAnalyticsAdapter.js @@ -35,16 +35,8 @@ function getPageInfo() { } function getUniqId() { - let cookies; - - try { - cookies = parseCookies(document.cookie); - } catch (a) { - cookies = {}; - } - let isUniqFromLS; - let uniq = cookies[ UNIQ_ID_KEY ]; + let uniq = storage.getCookie(UNIQ_ID_KEY); if (!uniq) { try { if (storage.hasLocalStorage()) { diff --git a/modules/insticatorBidAdapter.js b/modules/insticatorBidAdapter.js index 71815f058e7..5b7a64bc102 100644 --- a/modules/insticatorBidAdapter.js +++ b/modules/insticatorBidAdapter.js @@ -25,7 +25,7 @@ function getUserId() { let uid; if (storage.localStorageIsEnabled()) { - uid = localStorage.getItem(USER_ID_KEY); + uid = storage.getDataFromLocalStorage(USER_ID_KEY); } else { uid = storage.getCookie(USER_ID_KEY); } @@ -39,7 +39,7 @@ function getUserId() { function setUserId(userId) { if (storage.localStorageIsEnabled()) { - localStorage.setItem(USER_ID_KEY, userId); + storage.setDataInLocalStorage(USER_ID_KEY, userId); } if (storage.cookiesAreEnabled()) { diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 717a886a1f6..99434e18adb 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -381,7 +381,7 @@ function getUserIds(bidUserId) { function parseQueryStringParams() { let params = {}; try { - params = JSON.parse(localStorage.ivbs); + params = JSON.parse(readFromLocalStorage('ivbs')); } catch (e) { } let re = /[\\?&]([^=]+)=([^\\?&#]+)/g; diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 3027a8e75b8..72342903d87 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -9,7 +9,7 @@ const HOST = 'https://krk.kargo.com'; const SYNC = 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}&gdpr={GDPR}&gdpr_consent={GDPR_CONSENT}&us_privacy={US_PRIVACY}'; const SYNC_COUNT = 5; const GVLID = 972; -const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO] +const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); let sessionId, @@ -146,31 +146,9 @@ export const spec = { }); }, - // PRIVATE - _readCookie(name) { - if (!storage.cookiesAreEnabled()) { - return null; - } - let nameEquals = `${name}=`; - let cookies = document.cookie.split(';'); - - for (let i = 0; i < cookies.length; i++) { - let cookie = cookies[i]; - while (cookie.charAt(0) === ' ') { - cookie = cookie.substring(1, cookie.length); - } - - if (cookie.indexOf(nameEquals) === 0) { - return cookie.substring(nameEquals.length, cookie.length); - } - } - - return null; - }, - _getCrbFromCookie() { try { - const crb = JSON.parse(decodeURIComponent(spec._readCookie('krg_crb'))); + const crb = JSON.parse(storage.getCookie('krg_crb')); if (crb && crb.v) { let vParsed = JSON.parse(atob(crb.v)); if (vParsed) { @@ -242,7 +220,7 @@ export const spec = { userIDs: spec._getUserIds(tdid, usp, gdpr), // TODO: this should probably look at refererInfo pageURL: window.location.href, - rawCRB: spec._readCookie('krg_crb'), + rawCRB: storage.getCookie('krg_crb'), rawCRBLocalStorage: spec._getLocalStorageSafely('krg_crb') }; }, diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index a3061eb1c99..9b40d45542c 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -157,7 +157,7 @@ function extendUserSessionTimeout() { } function userSessionID() { - return storage.getDataFromLocalStorage(localStorageSessName()) ? localStorage.getItem(localStorageSessName()) : ''; + return storage.getDataFromLocalStorage(localStorageSessName()) || ''; } function sessionExpired() { diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 89bf4bb331d..01de23f7590 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -4,6 +4,7 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {includes as strIncludes} from '../src/polyfill.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'sspBC'; const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; @@ -11,6 +12,7 @@ const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TRACKER_URL = 'https://bdr.wpcdn.pl/tag/jstracker.js'; const GVLID = 676; +const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); const TMAX = 450; const BIDDER_VERSION = '5.6'; const DEFAULT_CURRENCY = 'PLN'; @@ -101,9 +103,7 @@ const getNotificationPayload = bidData => { const cookieSupport = () => { const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent); - const useCookies = navigator.cookieEnabled || !!document.cookie.length; - - return !isSafari && useCookies; + return !isSafari && storage.cookiesAreEnabled(); }; const applyClientHints = ortbRequest => { diff --git a/modules/widespaceBidAdapter.js b/modules/widespaceBidAdapter.js index ba94f90f9c9..ea6f1bce793 100644 --- a/modules/widespaceBidAdapter.js +++ b/modules/widespaceBidAdapter.js @@ -185,28 +185,6 @@ function storeData(data, name, stringify = true) { function getData(name, remove = true) { let data = []; - if (storage.hasLocalStorage()) { - Object.keys(localStorage).filter((key) => { - if (key.indexOf(name) > -1) { - data.push(storage.getDataFromLocalStorage(key)); - if (remove) { - storage.removeDataFromLocalStorage(key); - } - } - }); - } - - if (storage.cookiesAreEnabled()) { - document.cookie.split(';').forEach((item) => { - let value = item.split('='); - if (value[0].indexOf(name) > -1) { - data.push(value[1]); - if (remove) { - storage.setCookie(value[0], '', 'Thu, 01 Jan 1970 00:00:01 GMT'); - } - } - }); - } return data; } diff --git a/test/spec/modules/insticatorBidAdapter_spec.js b/test/spec/modules/insticatorBidAdapter_spec.js index 7d14742db85..fc7ed1833ac 100644 --- a/test/spec/modules/insticatorBidAdapter_spec.js +++ b/test/spec/modules/insticatorBidAdapter_spec.js @@ -183,6 +183,11 @@ describe('InsticatorBidAdapter', function () { let sandbox; beforeEach(() => { + $$PREBID_GLOBAL$$.bidderSettings = { + insticator: { + storageAllowed: true + } + }; getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); getCookieStub = sinon.stub(storage, 'getCookie'); @@ -198,6 +203,7 @@ describe('InsticatorBidAdapter', function () { localStorageIsEnabledStub.restore(); getCookieStub.restore(); cookiesAreEnabledStub.restore(); + $$PREBID_GLOBAL$$.bidderSettings = {}; }); const serverRequests = spec.buildRequests([bidRequest], bidderRequest); @@ -270,7 +276,6 @@ describe('InsticatorBidAdapter', function () { expect(data.regs.ext.gdpr).to.equal(1); expect(data.regs.ext.gdprConsentString).to.equal(bidderRequest.gdprConsent.consentString); expect(data.user).to.be.an('object'); - expect(data.user.id).to.equal(USER_ID_DUMMY_VALUE); expect(data.user).to.have.property('yob'); expect(data.user.yob).to.equal(1984); expect(data.user).to.have.property('gender'); diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index c7a14b3d5aa..e900b91ed11 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -161,7 +161,7 @@ describe('kargo adapter tests', function () { } function getKrgCrbOldStyle() { - return '%7B%22v%22%3A%22eyJzeW5jSWRzIjp7IjIiOiI4MmZhMjU1NS01OTY5LTQ2MTQtYjRjZS00ZGNmMTA4MGU5ZjkiLCIxNiI6IlZveElrOEFvSnowQUFFZENleUFBQUFDMiY1MDIiLCIyMyI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjI0IjoiVm94SWs4QW9KejBBQUVkQ2V5QUFBQUMyJjUwMiIsIjI1IjoiNWVlMjQxMzgtNWUwMy00YjlkLWE5NTMtMzhlODMzZjI4NDlmIiwiMl84MCI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjJfOTMiOiI1ZWUyNDEzOC01ZTAzLTRiOWQtYTk1My0zOGU4MzNmMjg0OWYifSwibGV4SWQiOiI1ZjEwODgzMS0zMDJkLTExZTctYmY2Yi00NTk1YWNkM2JmNmMiLCJjbGllbnRJZCI6IjI0MTBkOGYyLWMxMTEtNDgxMS04OGE1LTdiNWUxOTBlNDc1ZiIsIm9wdE91dCI6ZmFsc2UsImV4cGlyZVRpbWUiOjE0OTc0NDkzODI2NjgsImxhc3RTeW5jZWRBdCI6MTQ5NzM2Mjk3OTAxMn0=%22%7D'; + return '{"v":"eyJzeW5jSWRzIjp7IjIiOiI4MmZhMjU1NS01OTY5LTQ2MTQtYjRjZS00ZGNmMTA4MGU5ZjkiLCIxNiI6IlZveElrOEFvSnowQUFFZENleUFBQUFDMiY1MDIiLCIyMyI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjI0IjoiVm94SWs4QW9KejBBQUVkQ2V5QUFBQUMyJjUwMiIsIjI1IjoiNWVlMjQxMzgtNWUwMy00YjlkLWE5NTMtMzhlODMzZjI4NDlmIiwiMl84MCI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjJfOTMiOiI1ZWUyNDEzOC01ZTAzLTRiOWQtYTk1My0zOGU4MzNmMjg0OWYifSwibGV4SWQiOiI1ZjEwODgzMS0zMDJkLTExZTctYmY2Yi00NTk1YWNkM2JmNmMiLCJjbGllbnRJZCI6IjI0MTBkOGYyLWMxMTEtNDgxMS04OGE1LTdiNWUxOTBlNDc1ZiIsIm9wdE91dCI6ZmFsc2UsImV4cGlyZVRpbWUiOjE0OTc0NDkzODI2NjgsImxhc3RTeW5jZWRBdCI6MTQ5NzM2Mjk3OTAxMn0="}'; } function initializeKrgCrb(cookieOnly) { @@ -188,7 +188,7 @@ describe('kargo adapter tests', function () { } function getInvalidKrgCrbType2OldStyle() { - return '%7B%22v%22%3A%22%26%26%26%26%26%26%22%7D'; + return '{"v":"&&&&&&"}'; } function initializeInvalidKrgCrbType2() { @@ -200,7 +200,7 @@ describe('kargo adapter tests', function () { } function getInvalidKrgCrbType3OldStyle() { - return '%7B%22v%22%3A%22Ly8v%22%7D'; + return '{"v":"Ly8v"}'; } function initializeInvalidKrgCrbType3Cookie() { @@ -208,7 +208,7 @@ describe('kargo adapter tests', function () { } function getInvalidKrgCrbType4OldStyle() { - return '%7B%22v%22%3A%22bnVsbA%3D%3D%22%7D'; + return '{"v":"bnVsbA=="}'; } function initializeInvalidKrgCrbType4Cookie() { @@ -220,7 +220,7 @@ describe('kargo adapter tests', function () { } function getEmptyKrgCrbOldStyle() { - return '%7B%22v%22%3A%22eyJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9%22%7D'; + return '{"v":"eyJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9"}'; } function initializeEmptyKrgCrb() { @@ -593,6 +593,11 @@ describe('kargo adapter tests', function () { var shouldSimulateOutdatedBrowser, crb, isActuallyOutdatedBrowser; beforeEach(() => { + $$PREBID_GLOBAL$$.bidderSettings = { + kargo: { + storageAllowed: true + } + }; crb = {}; shouldSimulateOutdatedBrowser = false; isActuallyOutdatedBrowser = false; From 32f257abf82c90f1409383513b93e1f688cf7b27 Mon Sep 17 00:00:00 2001 From: Andrew Slagle <42588549+spotxslagle@users.noreply.github.com> Date: Fri, 22 Jul 2022 12:09:04 -0600 Subject: [PATCH 057/569] Prebid Core: allows immediate rendering (#8633) * Renderer allows immediate rendering * Add unit test for immediate rendering --- src/Renderer.js | 16 +++++++++------- test/spec/renderer_spec.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/Renderer.js b/src/Renderer.js index f26a5a377c0..3375edcd6c3 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -14,7 +14,7 @@ const moduleCode = 'outstream'; */ export function Renderer(options) { - const { url, config, id, callback, loaded, adUnitCode } = options; + const { url, config, id, callback, loaded, adUnitCode, renderNow } = options; this.url = url; this.config = config; this.handlers = {}; @@ -50,19 +50,21 @@ export function Renderer(options) { } } - if (!isRendererPreferredFromAdUnit(adUnitCode)) { + if (isRendererPreferredFromAdUnit(adUnitCode)) { + logWarn(`External Js not loaded by Renderer since renderer url and callback is already defined on adUnit ${adUnitCode}`); + runRender(); + } else if (renderNow) { + runRender(); + } else { // we expect to load a renderer url once only so cache the request to load script this.cmd.unshift(runRender) // should render run first ? loadExternalScript(url, moduleCode, this.callback, this.documentContext); - } else { - logWarn(`External Js not loaded by Renderer since renderer url and callback is already defined on adUnit ${adUnitCode}`); - runRender() } }.bind(this) // bind the function to this object to avoid 'this' errors } -Renderer.install = function({ url, config, id, callback, loaded, adUnitCode }) { - return new Renderer({ url, config, id, callback, loaded, adUnitCode }); +Renderer.install = function({ url, config, id, callback, loaded, adUnitCode, renderNow }) { + return new Renderer({ url, config, id, callback, loaded, adUnitCode, renderNow }); }; Renderer.prototype.getConfig = function() { diff --git a/test/spec/renderer_spec.js b/test/spec/renderer_spec.js index 6de06606136..c41334f916a 100644 --- a/test/spec/renderer_spec.js +++ b/test/spec/renderer_spec.js @@ -107,6 +107,21 @@ describe('Renderer', function () { sinon.assert.calledOnce(func2); expect(testRenderer1.cmd.length).to.equal(0); }); + + it('renders immediately when requested', function () { + const testRenderer3 = Renderer.install({ + url: 'https://httpbin.org/post', + config: { test: 'config2' }, + id: 2, + renderNow: true + }); + const func1 = sinon.spy(); + const testArg = 'testArgument'; + + testRenderer3.setRender(func1); + testRenderer3.render(testArg); + func1.calledWith(testArg).should.be.ok; + }); }); describe('3rd party renderer', function () { From 8dccf2b447998457edd79e4c2a18ad1f928e0d48 Mon Sep 17 00:00:00 2001 From: Andrew Slagle <42588549+spotxslagle@users.noreply.github.com> Date: Sat, 23 Jul 2022 09:57:19 -0600 Subject: [PATCH 058/569] Prebid Core : add setScriptAttributes to adloader in Utils (#8624) * Allow SpotX module to load script without appendChild * Fix SpotX spec to no longer use appendChild * Restore spotx files * Formatting fixes * Unit test for setScriptAttributes * Unit test and formatting fix * Formatting fix * Comment that attributes are added once only --- src/adloader.js | 14 ++++++++++---- src/utils.js | 13 +++++++++++++ test/spec/adloader_spec.js | 23 +++++++++++++++++++++++ test/spec/utils_spec.js | 16 ++++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/adloader.js b/src/adloader.js index 28acec136f6..1e7995a9dc6 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -1,5 +1,5 @@ import {includes} from './polyfill.js'; -import { logError, logWarn, insertElement } from './utils.js'; +import { logError, logWarn, insertElement, setScriptAttributes } from './utils.js'; const _requestCache = new WeakMap(); // The below list contains modules or vendors whom Prebid allows to load external JS. @@ -9,6 +9,7 @@ const _approvedLoadExternalJSList = [ 'criteo', 'outstream', 'adagio', + 'spotx', 'browsi', 'brandmetrics', 'justtag', @@ -27,8 +28,9 @@ const _approvedLoadExternalJSList = [ * @param {string} moduleCode bidderCode or module code of the module requesting this resource * @param {function} [callback] callback function to be called after the script is loaded * @param {Document} [doc] the context document, in which the script will be loaded, defaults to loaded document + * @param {object} an object of attributes to be added to the script with setAttribute by [key] and [value]; Only the attributes passed in the first request of a url will be added. */ -export function loadExternalScript(url, moduleCode, callback, doc) { +export function loadExternalScript(url, moduleCode, callback, doc, attributes) { if (!moduleCode || !url) { logError('cannot load external script without url and moduleCode'); return; @@ -77,9 +79,9 @@ export function loadExternalScript(url, moduleCode, callback, doc) { } catch (e) { logError('Error executing callback', 'adloader.js:loadExternalScript', e); } - }, doc); + }, doc, attributes); - function requestResource(tagSrc, callback, doc) { + function requestResource(tagSrc, callback, doc, attributes) { if (!doc) { doc = document; } @@ -107,6 +109,10 @@ export function loadExternalScript(url, moduleCode, callback, doc) { jptScript.src = tagSrc; + if (attributes) { + setScriptAttributes(jptScript, attributes); + } + // add the new script tag to the page insertElement(jptScript, doc); diff --git a/src/utils.js b/src/utils.js index b53475af912..9434a163133 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1368,3 +1368,16 @@ export function safeJSONParse(data) { return JSON.parse(data); } catch (e) {} } + +/** + * Sets dataset attributes on a script + * @param {Script} script + * @param {object} attributes + */ +export function setScriptAttributes(script, attributes) { + for (let key in attributes) { + if (attributes.hasOwnProperty(key)) { + script.setAttribute(key, attributes[key]); + } + } +} diff --git a/test/spec/adloader_spec.js b/test/spec/adloader_spec.js index 0c50e66c63c..b775ec76e9b 100644 --- a/test/spec/adloader_spec.js +++ b/test/spec/adloader_spec.js @@ -69,4 +69,27 @@ describe('adLoader', function () { expect(utilsinsertElementStub.callCount).to.equal(2); }); }); + + it('attaches passed attributes to a script', function () { + const doc = { + createElement: function () { + return { + setAttribute: function (key, value) { + this[key] = value; + } + } + }, + getElementsByTagName: function() { + return { + firstChild: { + insertBefore: function() {} + } + } + } + }, + attrs = {'z': 'A', 'y': 2}; + let script = adLoader.loadExternalScript('someUrl', 'criteo', undefined, doc, attrs); + expect(script.z).to.equal('A'); + expect(script.y).to.equal(2); + }); }); diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 50a95b079c9..500f479355d 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1243,4 +1243,20 @@ describe('Utils', function () { }); }); }); + + describe('setScriptAttributes', () => { + it('correctly adds attributes from an object', () => { + const script = document.createElement('script'), + attrs = { + 'data-first_prop': '1', + 'data-second_prop': 'b', + 'id': 'newId' + }; + script.id = 'oldId'; + utils.setScriptAttributes(script, attrs); + expect(script.dataset['first_prop']).to.equal('1'); + expect(script.dataset.second_prop).to.equal('b'); + expect(script.id).to.equal('newId'); + }); + }); }); From 4603f96b36bd915e2f1054addb93b7281b4914e0 Mon Sep 17 00:00:00 2001 From: Andrew Slagle <42588549+spotxslagle@users.noreply.github.com> Date: Sat, 23 Jul 2022 09:57:59 -0600 Subject: [PATCH 059/569] SpotX Adapter: Render immediately (#8731) * Render spotx ads immediately * Formatting fix --- modules/spotxBidAdapter.js | 2 +- test/spec/modules/spotxBidAdapter_spec.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 86a37e97e2f..bcc65c85791 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -379,7 +379,7 @@ export const spec = { const playersize = deepAccess(currentBidRequest, 'mediaTypes.video.playerSize'); const renderer = Renderer.install({ id: 0, - url: '/', + renderNow: true, config: { adText: 'SpotX Outstream Video Ad via Prebid.js', player_width: playersize[0][0], diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index 9d961657ac2..274ef60471b 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -442,6 +442,7 @@ describe('the spotx adapter', function () { }, bidId: 123, params: { + ad_unit: 'outstream', player_width: 400, player_height: 300, content_page_url: 'prebid.js', @@ -533,6 +534,17 @@ describe('the spotx adapter', function () { expect(responses[1].videoCacheKey).to.equal('cache124'); expect(responses[1].width).to.equal(200); }); + + it('should set the renderer attached to the bid to render immediately', function () { + var renderer = spec.interpretResponse(serverResponse, bidderRequestObj)[0].renderer, + hasRun = false; + expect(renderer._render).to.be.a('function'); + renderer._render = () => { + hasRun = true; + } + renderer.render(); + expect(hasRun).to.equal(true); + }); }); describe('outstreamRender', function() { From 2eee4f13951b66a731fb146aa077b62e9de6c2c1 Mon Sep 17 00:00:00 2001 From: OronW <41260031+OronW@users.noreply.github.com> Date: Mon, 25 Jul 2022 14:52:48 +0300 Subject: [PATCH 060/569] added logic to detect the loop number (#8733) --- modules/kueezBidAdapter.js | 1 + test/spec/modules/kueezBidAdapter_spec.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/modules/kueezBidAdapter.js b/modules/kueezBidAdapter.js index f1156376ff5..4e0278603fd 100644 --- a/modules/kueezBidAdapter.js +++ b/modules/kueezBidAdapter.js @@ -244,6 +244,7 @@ function generateBidParameters(bid, bidderRequest) { const bidObject = { adUnitCode: getBidIdParameter('adUnitCode', bid), bidId: getBidIdParameter('bidId', bid), + loop: getBidIdParameter('bidderRequestsCount', bid), bidderRequestId: getBidIdParameter('bidderRequestId', bid), floorPrice: Math.max(getFloorPrice(bid, mediaType), paramsFloorPrice), mediaType, diff --git a/test/spec/modules/kueezBidAdapter_spec.js b/test/spec/modules/kueezBidAdapter_spec.js index 16440965765..cd95a9ebdc6 100644 --- a/test/spec/modules/kueezBidAdapter_spec.js +++ b/test/spec/modules/kueezBidAdapter_spec.js @@ -53,6 +53,7 @@ describe('kueezBidAdapter', function () { 'org': 'test-publisher-id' }, 'bidId': '5wfg9887sd5478', + 'loop': 1, 'bidderRequestId': 'op87952ewq8567', 'auctionId': '87se98rt-5789-8735-2546-t98yh5678231', 'mediaTypes': { @@ -71,6 +72,7 @@ describe('kueezBidAdapter', function () { 'org': 'test-publisher-id' }, 'bidId': '5wfg9887sd5478', + 'loop': 1, 'bidderRequestId': 'op87952ewq8567', 'auctionId': '87se98rt-5789-8735-2546-t98yh5678231', 'mediaTypes': { @@ -91,6 +93,7 @@ describe('kueezBidAdapter', function () { 'testMode': true }, 'bidId': '5wfg9887sd5478', + 'loop': 2, 'bidderRequestId': 'op87952ewq8567', 'auctionId': '87se98rt-5789-8735-2546-t98yh5678231', } From 40e5747b746b79939a98dce2f21c7cb09e5da0c4 Mon Sep 17 00:00:00 2001 From: mmprebid <67095277+mmprebid@users.noreply.github.com> Date: Mon, 25 Jul 2022 17:25:58 +0530 Subject: [PATCH 061/569] Truereach Bid adapter: update url (#8728) * Added Trureach Prebid Adapter * cleaned up truereach bidder adapter for release * truereach bidder adapter md file for release * [truereach] bidder adapter and md files update. bidderUrl no more configurable. * [Prebid] supporting nurl * [Prebid] changes required due to code style * [Prebid] prebid unit test * [Prebid] added advertiserDomains in response object * [Prebid] Secure Bidder Url. * Added usersync support * changes in bidder url * add singapore region for bidder url & handle https in usersync url * add singapore region in usersync url Co-authored-by: Nitin Kumar Co-authored-by: arnav Co-authored-by: arnav Co-authored-by: nawedita.keshari@momagic.com-at-143515221912 --- modules/truereachBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/truereachBidAdapter.js b/modules/truereachBidAdapter.js index a0244e3a349..46774275bba 100755 --- a/modules/truereachBidAdapter.js +++ b/modules/truereachBidAdapter.js @@ -5,7 +5,7 @@ import { BANNER } from '../src/mediaTypes.js'; const SUPPORTED_AD_TYPES = [BANNER]; const BIDDER_CODE = 'truereach'; -const BIDDER_URL = 'https://ads.momagic.com/exchange/openrtb25/'; +const BIDDER_URL = 'https://ads-sg.momagic.com/exchange/openrtb25/'; export const spec = { code: BIDDER_CODE, @@ -94,7 +94,7 @@ export const spec = { if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: 'http://ads.momagic.com/jsp/usersync.jsp' + gdprParams + url: 'https://ads-sg.momagic.com/jsp/usersync.jsp' + gdprParams }); } return syncs; From 6e3b732b9a26ef9786fcca84c5b07fa7fd9c9cae Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Mon, 25 Jul 2022 05:27:05 -0700 Subject: [PATCH 062/569] Truereach Bid Adapter: update unit test url (#8735) * update test url * https update --- test/spec/modules/truereachBidAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/truereachBidAdapter_spec.js b/test/spec/modules/truereachBidAdapter_spec.js index 3c78c4b848d..cd7d0873569 100644 --- a/test/spec/modules/truereachBidAdapter_spec.js +++ b/test/spec/modules/truereachBidAdapter_spec.js @@ -83,7 +83,7 @@ describe('truereachBidAdapterTests', function () { }); describe('user_sync', function() { - const user_sync_url = 'http://ads.momagic.com/jsp/usersync.jsp'; + const user_sync_url = 'https://ads-sg.momagic.com/jsp/usersync.jsp'; it('register_iframe_pixel_if_iframeEnabled_is_true', function() { let syncs = spec.getUserSyncs( {iframeEnabled: true} From 25aa858e570ab30a7aeb6ebd3176de32133f6673 Mon Sep 17 00:00:00 2001 From: adquery <89853721+adquery@users.noreply.github.com> Date: Mon, 25 Jul 2022 15:51:58 +0200 Subject: [PATCH 063/569] Adquery Bid Adapter: bugfix local uuid (#8734) * adquery - bugfix local uuid * adquery - bugfix local uuid Co-authored-by: m.czerwiak --- modules/adqueryBidAdapter.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/adqueryBidAdapter.js b/modules/adqueryBidAdapter.js index e449adce1a1..048e43641f0 100644 --- a/modules/adqueryBidAdapter.js +++ b/modules/adqueryBidAdapter.js @@ -58,6 +58,7 @@ export const spec = { logInfo(request); logInfo(response); + let qid = null; const res = response && response.body && response.body.data; let bidResponses = []; @@ -86,6 +87,17 @@ export const spec = { bidResponses.push(bidResponse); logInfo('bidResponses', bidResponses); + if (res && res.qid) { + if (storage.getDataFromLocalStorage('qid')) { + qid = storage.getDataFromLocalStorage('qid'); + if (qid && qid.includes('%7B%22')) { + storage.setDataInLocalStorage('qid', res.qid); + } + } else { + storage.setDataInLocalStorage('qid', res.qid); + } + } + return bidResponses; }, @@ -178,25 +190,13 @@ export const spec = { }; function buildRequest(validBidRequests, bidderRequest) { - let gnerateQid = (Math.random().toString(36).substring(2)) + ((new Date().getTime()).toString(36)); let bid = validBidRequests; - let qid = gnerateQid; - - if (storage.getDataFromLocalStorage('qid')) { - qid = storage.getDataFromLocalStorage('qid'); - if (qid && qid.includes('%7B%22')) { - qid = gnerateQid; - } - } else { - storage.setDataInLocalStorage('qid', qid); - } - return { placementCode: bid.params.placementId, auctionId: bid.auctionId, - qid: qid, type: bid.params.type, adUnitCode: bid.adUnitCode, + bidQid: storage.getDataFromLocalStorage('qid') || null, bidId: bid.bidId, bidder: bid.bidder, bidderRequestId: bid.bidderRequestId, From b11951206833cd4f50eb6622405e35b6e4a5e643 Mon Sep 17 00:00:00 2001 From: azuryo <108641852+azuryo@users.noreply.github.com> Date: Tue, 26 Jul 2022 00:19:58 +0900 Subject: [PATCH 064/569] Miocroad Bid Adapter: Added Support for Audience IDs (#8662) * Microad Bid Adapter: Add IM-UID * Microad Bid Adapter: Add ID5 ID * Microad Bid Adapter: Add Unified ID * Microad Bid Adapter: Add Novatiq Hyper ID * Microad Bid Adapter: Add Parrable ID * Microad Bid Adapter: Add AudienceOne User ID * Microad BidAdapter: Add Audience ID spec * Microad BidAdapter: Add Ramp ID in params * Microad BidAdapter: Fix Audience ID * Microad BidAdapter: Add Criteo ID and Shared ID * Microad BidAdapter: Fix typo * Microad BidAdapter: Fix variable name --- modules/microadBidAdapter.js | 26 +++++++- test/spec/modules/microadBidAdapter_spec.js | 71 +++++++++++++++------ 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index 77710584f41..d5116b1dcb0 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -22,6 +22,18 @@ const BANNER_CODE = 1; const NATIVE_CODE = 2; const VIDEO_CODE = 4; +const AUDIENCE_IDS = [ + {type: 6, bidKey: 'userId.imuid'}, + {type: 8, bidKey: 'userId.id5id.uid'}, + {type: 9, bidKey: 'userId.tdid'}, + {type: 10, bidKey: 'userId.novatiq.snowflake'}, + {type: 11, bidKey: 'userId.parrableId.eid'}, + {type: 12, bidKey: 'userId.dacId.id'}, + {type: 13, bidKey: 'userId.idl_env'}, + {type: 14, bidKey: 'userId.criteoId'}, + {type: 15, bidKey: 'userId.pubcid'} +] + function createCBT() { const randomValue = Math.floor(Math.random() * Math.pow(10, 18)).toString(16); const date = new Date().getTime().toString(16); @@ -83,9 +95,17 @@ export const spec = { } } - const idlEnv = deepAccess(bid, 'userId.idl_env') - if (!isEmpty(idlEnv) && isStr(idlEnv)) { - params['idl_env'] = idlEnv + const aidsParams = [] + AUDIENCE_IDS.forEach((audienceId) => { + const bidAudienceId = deepAccess(bid, audienceId.bidKey); + if (!isEmpty(bidAudienceId) && isStr(bidAudienceId)) { + aidsParams.push({ type: audienceId.type, id: bidAudienceId }); + // Set Ramp ID + if (audienceId.type === 13) params['idl_env'] = bidAudienceId; + } + }) + if (aidsParams.length > 0) { + params['aids'] = JSON.stringify(aidsParams) } requests.push({ diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js index ff0b705594b..cca337c83f2 100644 --- a/test/spec/modules/microadBidAdapter_spec.js +++ b/test/spec/modules/microadBidAdapter_spec.js @@ -266,35 +266,70 @@ describe('microadBidAdapter', () => { }); }); - it('should add Liveramp identity link if it is available in request parameters', () => { - const bidRequestWithLiveramp = Object.assign({}, bidRequestTemplate, { - userId: {idl_env: 'idl-env-sample'} + it('should not add Liveramp identity link and Audience ID if it is not available in request parameters', () => { + const bidRequestWithOutLiveramp = Object.assign({}, bidRequestTemplate, { + userId: {} }); - const requests = spec.buildRequests([bidRequestWithLiveramp], bidderRequest) + const requests = spec.buildRequests([bidRequestWithOutLiveramp], bidderRequest) requests.forEach(request => { expect(request.data).to.deep.equal( Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt, - idl_env: 'idl-env-sample' + cbt: request.data.cbt }) ); }) }); - it('should not add Liveramp identity link if it is not available in request parameters', () => { - const bidRequestWithLiveramp = Object.assign({}, bidRequestTemplate, { - userId: {} - }); - const requests = spec.buildRequests([bidRequestWithLiveramp], bidderRequest) - const expectedResult = Object.assign({}, expectedResultTemplate) - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt + Object.entries({ + 'IM-UID': { + userId: {imuid: 'imuid-sample'}, + expected: {aids: JSON.stringify([{type: 6, id: 'imuid-sample'}])} + }, + 'ID5 ID': { + userId: {id5id: {uid: 'id5id-sample'}}, + expected: {aids: JSON.stringify([{type: 8, id: 'id5id-sample'}])} + }, + 'Unified ID': { + userId: {tdid: 'unified-sample'}, + expected: {aids: JSON.stringify([{type: 9, id: 'unified-sample'}])} + }, + 'Novatiq Snowflake ID': { + userId: {novatiq: {snowflake: 'novatiq-sample'}}, + expected: {aids: JSON.stringify([{type: 10, id: 'novatiq-sample'}])} + }, + 'Parrable ID': { + userId: {parrableId: {eid: 'parrable-sample'}}, + expected: {aids: JSON.stringify([{type: 11, id: 'parrable-sample'}])} + }, + 'AudienceOne User ID': { + userId: {dacId: {id: 'audience-one-sample'}}, + expected: {aids: JSON.stringify([{type: 12, id: 'audience-one-sample'}])} + }, + 'Ramp ID and Liveramp identity': { + userId: {idl_env: 'idl-env-sample'}, + expected: {idl_env: 'idl-env-sample', aids: JSON.stringify([{type: 13, id: 'idl-env-sample'}])} + }, + 'Criteo ID': { + userId: {criteoId: 'criteo-id-sample'}, + expected: {aids: JSON.stringify([{type: 14, id: 'criteo-id-sample'}])} + }, + 'Shared ID': { + userId: {pubcid: 'shared-id-sample'}, + expected: {aids: JSON.stringify([{type: 15, id: 'shared-id-sample'}])} + } + }).forEach(([test, arg]) => { + it(`should add ${test} if it is available in request parameters`, () => { + const bidRequestWithUserId = { ...bidRequestTemplate, userId: arg.userId } + const requests = spec.buildRequests([bidRequestWithUserId], bidderRequest) + requests.forEach((request) => { + expect(request.data).to.deep.equal({ + ...expectedResultTemplate, + cbt: request.data.cbt, + ...arg.expected }) - ); + }) }) - }); + }) }); describe('interpretResponse', () => { From 715a1f5fc07ac273a729dc3a6d6625a05468b502 Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Mon, 25 Jul 2022 09:02:06 -0700 Subject: [PATCH 065/569] Data Controller Module: initial release (#8484) * Data Controller Module * Data Controller Module * Correcting typo * Setting hook based on data controller configuration * Setting hook based on data controller configuration * Updating to filter data before startAuction event * Updating to filter data before startAuction event * Review comment fixes * Review comment fixes Co-authored-by: skocheri --- modules/dataControllerModule/index.js | 195 +++++++++++++++++++++ modules/dataControllerModule/index.md | 29 ++++ test/spec/modules/dataController_spec.js | 210 +++++++++++++++++++++++ 3 files changed, 434 insertions(+) create mode 100644 modules/dataControllerModule/index.js create mode 100644 modules/dataControllerModule/index.md create mode 100644 test/spec/modules/dataController_spec.js diff --git a/modules/dataControllerModule/index.js b/modules/dataControllerModule/index.js new file mode 100644 index 00000000000..c9d15457c3d --- /dev/null +++ b/modules/dataControllerModule/index.js @@ -0,0 +1,195 @@ +/** + * This module validates the configuration and filters data accordingly + * @module modules/dataController + */ +import {config} from '../../src/config.js'; +import {getHook, module} from '../../src/hook.js'; +import {deepAccess, deepSetValue, prefixLog} from '../../src/utils.js'; +import {startAuction} from '../../src/prebid.js'; + +const LOG_PRE_FIX = 'Data_Controller : '; +const ALL = '*'; +const MODULE_NAME = 'dataController'; +const GLOBAL = {}; +let _dataControllerConfig; + +const _logger = prefixLog(LOG_PRE_FIX); + +/** + * BidderRequests hook to intiate module and reset data object + */ +export function filterBidData(fn, req) { + if (_dataControllerConfig.filterEIDwhenSDA) { + filterEIDs(req.adUnits, req.ortb2Fragments); + } + + if (_dataControllerConfig.filterSDAwhenEID) { + filterSDA(req.adUnits, req.ortb2Fragments); + } + fn.call(this, req); + return req; +} + +function containsConfiguredEIDS(eidSourcesMap, bidderCode) { + if (_dataControllerConfig.filterSDAwhenEID.includes(ALL)) { + return true; + } + let bidderEIDs = eidSourcesMap.get(bidderCode); + if (bidderEIDs == undefined) { + return false; + } + let containsEIDs = false; + _dataControllerConfig.filterSDAwhenEID.some(source => { + if (bidderEIDs.has(source)) { + containsEIDs = true; + } + }); + return containsEIDs; +} + +function containsConfiguredSDA(segementMap, bidderCode) { + if (_dataControllerConfig.filterEIDwhenSDA.includes(ALL)) { + return true; + } + return hasValue(segementMap.get(bidderCode)) || hasValue(segementMap.get(GLOBAL)) +} + +function hasValue(bidderSegement) { + let containsSDA = false; + if (bidderSegement == undefined) { + return false; + } + _dataControllerConfig.filterEIDwhenSDA.some(segment => { + if (bidderSegement.has(segment)) { + containsSDA = true; + } + }); + return containsSDA; +} + +function getSegmentConfig(ortb2Fragments) { + let bidderSDAMap = new Map(); + let globalObject = deepAccess(ortb2Fragments, 'global') || {}; + + collectSegments(bidderSDAMap, GLOBAL, globalObject); + if (ortb2Fragments.bidder) { + for (const [key, value] of Object.entries(ortb2Fragments.bidder)) { + collectSegments(bidderSDAMap, key, value); + } + } + return bidderSDAMap; +} + +function collectSegments(bidderSDAMap, key, data) { + let segmentSet = constructSegment(deepAccess(data, 'user.data') || []); + if (segmentSet && segmentSet.size > 0) bidderSDAMap.set(key, segmentSet); +} + +function constructSegment(userData) { + let segmentSet; + if (userData) { + segmentSet = new Set(); + for (let i = 0; i < userData.length; i++) { + let segments = userData[i].segment; + let segmentPrefix = ''; + if (userData[i].name) { + segmentPrefix = userData[i].name + ':'; + } + + if (userData[i].ext && userData[i].ext.segtax) { + segmentPrefix += userData[i].ext.segtax + ':'; + } + for (let j = 0; j < segments.length; j++) { + segmentSet.add(segmentPrefix + segments[j].id); + } + } + } + + return segmentSet; +} + +function getEIDsSource(adUnits) { + let bidderEIDSMap = new Map(); + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + let userEIDs = deepAccess(bid, 'userIdAsEids') || []; + + if (userEIDs) { + let sourceSet = new Set(); + for (let i = 0; i < userEIDs.length; i++) { + let source = userEIDs[i].source; + sourceSet.add(source); + } + bidderEIDSMap.set(bid.bidder, sourceSet); + } + }); + }); + + return bidderEIDSMap; +} + +function filterSDA(adUnits, ortb2Fragments) { + let bidderEIDSMap = getEIDsSource(adUnits); + let resetGlobal = false; + for (const [key, value] of Object.entries(ortb2Fragments.bidder)) { + let resetSDA = containsConfiguredEIDS(bidderEIDSMap, key); + if (resetSDA) { + deepSetValue(value, 'user.data', []); + resetGlobal = true; + } + } + if (resetGlobal) { + deepSetValue(ortb2Fragments, 'global.user.data', []) + } +} + +function filterEIDs(adUnits, ortb2Fragments) { + let segementMap = getSegmentConfig(ortb2Fragments); + let globalEidUpdate = false; + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + let resetEID = containsConfiguredSDA(segementMap, bid.bidder); + if (resetEID) { + globalEidUpdate = true; + bid.userIdAsEids = []; + bid.userId = {}; + if (ortb2Fragments.bidder) { + let bidderFragment = ortb2Fragments.bidder[bid.bidder]; + let userExt = deepAccess(bidderFragment, 'user.ext.eids') || []; + if (userExt) { + deepSetValue(bidderFragment, 'user.ext.eids', []) + } + } + } + }); + }); + + if (globalEidUpdate) { + deepSetValue(ortb2Fragments, 'global.user.ext.eids', []) + } + return adUnits; +} + +export function init() { + const confListener = config.getConfig(MODULE_NAME, dataControllerConfig => { + const dataController = dataControllerConfig && dataControllerConfig.dataController; + if (!dataController) { + _logger.logInfo(`Data Controller is not configured`); + startAuction.getHooks({hook: filterBidData}).remove(); + return; + } + + if (dataController.filterEIDwhenSDA && dataController.filterSDAwhenEID) { + _logger.logInfo(`Data Controller can be configured with either filterEIDwhenSDA or filterSDAwhenEID`); + startAuction.getHooks({hook: filterBidData}).remove(); + return; + } + confListener(); // unsubscribe config listener + _dataControllerConfig = dataController; + + getHook('startAuction').before(filterBidData); + }); +} + +init(); +module(MODULE_NAME, init); diff --git a/modules/dataControllerModule/index.md b/modules/dataControllerModule/index.md new file mode 100644 index 00000000000..8714b886b0e --- /dev/null +++ b/modules/dataControllerModule/index.md @@ -0,0 +1,29 @@ +# Overview + +``` +Module Name: Data Controller Module +``` + +# Description + +This module will filter EIDs and SDA based on the configurations. + +Sub module object with the following keys: + +| param name | type | Scope | Description | Params | +| :------------ | :------------ | :------ | :------ | :------ | +| filterEIDwhenSDA | function | optional | Filters user EIDs based on SDA | bidrequest | +| filterSDAwhenEID | function | optional | Filters SDA based on configured EIDs | bidrequest | + +# Module Control Configuration + +``` + +pbjs.setConfig({ + dataController: { + filterEIDwhenSDA: ['*'] + filterSDAwhenEID: ['id5-sync.com'] + } +}); + +``` diff --git a/test/spec/modules/dataController_spec.js b/test/spec/modules/dataController_spec.js new file mode 100644 index 00000000000..25f55047377 --- /dev/null +++ b/test/spec/modules/dataController_spec.js @@ -0,0 +1,210 @@ +import {expect} from 'chai'; +import {config} from 'src/config.js'; +import {filterBidData, init} from 'modules/dataControllerModule/index.js'; +import {startAuction} from 'src/prebid.js'; + +describe('data controller', function () { + let spyFn; + + beforeEach(function () { + spyFn = sinon.spy(); + }); + + afterEach(function () { + config.resetConfig(); + }); + + describe('data controller', function () { + let result; + let callbackFn; + let req; + + beforeEach(function () { + init(); + result = null; + req = { + 'adUnits': [{ + 'bids': [ + { + 'bidder': 'ix', + 'userId': { + 'id5id': { + 'uid': 'ID5*19RudTU8mWiRdKfG-0E9oyyJCdbgb8MvcaEtPAxM29QZYT5DW9r01vBozMD93UZy', + 'ext': { + 'linkType': 2 + } + } + }, + 'userIdAsEids': [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': 'ID5*UJzjz7J0FNIWPCp8fAmwGavBhGxnJ06V9umghosEVm4ZPjpn2iWahAoiPal59yKa', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + } + ] + } + ], + + } + ] + }], + 'ortb2Fragments': { + 'bidder': { + 'ix': { + 'user': { + 'data': [ + { + 'name': 'permutive.com', + 'ext': { + 'segtax': 4 + }, + 'segment': [ + { + 'id': '777777' + }, + { + 'id': '888888' + } + ] + } + ] + } + } + } + } + }; + callbackFn = function (request) { + result = request; + }; + }); + + afterEach(function () { + config.resetConfig(); + startAuction.getHooks({hook: filterBidData}).remove(); + }); + + it('filterEIDwhenSDA for All SDA ', function () { + let dataControllerConfiguration = { + 'dataController': { + filterEIDwhenSDA: ['*'] + } + }; + config.setConfig(dataControllerConfiguration); + filterBidData(callbackFn, req); + expect(req.adUnits[0].bids[0].userIdAsEids).that.is.empty; + expect(req.adUnits[0].bids[0].userId).that.is.empty; + expect(req.ortb2Fragments.bidder.ix.user.ext.eids).that.is.empty; + expect(req.ortb2Fragments.global.user.ext.eids).that.is.empty; + }); + + it('filterEIDwhenSDA for available SAD permutive.com:4:777777 ', function () { + let dataControllerConfiguration = { + 'dataController': { + filterEIDwhenSDA: ['permutive.com:4:777777'] + } + + }; + config.setConfig(dataControllerConfiguration); + filterBidData(callbackFn, req); + expect(req.adUnits[0].bids[0].userIdAsEids).that.is.empty; + expect(req.adUnits[0].bids[0].userId).that.is.empty; + + expect(req.ortb2Fragments.bidder.ix.user.ext.eids).that.is.empty; + expect(req.ortb2Fragments.global.user.ext.eids).that.is.empty; + }); + + it('filterEIDwhenSDA for unavailable SAD test.com:4:9999 ', function () { + let dataControllerConfiguration = { + 'dataController': { + filterEIDwhenSDA: ['test.com:4:99999'] + } + }; + config.setConfig(dataControllerConfiguration); + filterBidData(callbackFn, req); + expect(req.adUnits[0].bids[0].userIdAsEids).that.is.not.empty; + expect(req.adUnits[0].bids[0].userId).that.is.not.empty; + }); + // Test for global + it('filterEIDwhenSDA for available global SAD test.com:4:777777 ', function () { + let dataControllerConfiguration = { + 'dataController': { + filterEIDwhenSDA: ['test.com:5:11111'] + } + + }; + config.setConfig(dataControllerConfiguration); + let globalObject = { + 'ortb2Fragments': { + 'global': { + 'user': { + 'yob': 1985, + 'gender': 'm', + 'keywords': 'a,b', + 'data': [ + { + 'name': 'test.com', + 'ext': { + 'segtax': 5 + }, + 'segment': [ + { + 'id': '11111' + }, + { + 'id': '22222' + } + ] + } + ] + } + } + } + }; + let globalRequest = Object.assign({}, req, globalObject); + filterBidData(callbackFn, globalRequest); + expect(globalRequest.adUnits[0].bids[0].userIdAsEids).that.is.empty; + expect(globalRequest.adUnits[0].bids[0].userId).that.is.empty; + }); + + it('filterSDAwhenEID for id5-sync.com EID ', function () { + let dataControllerConfiguration = { + 'dataController': { + filterSDAwhenEID: ['id5-sync.com'] + } + }; + config.setConfig(dataControllerConfiguration); + filterBidData(callbackFn, req); + expect(req.ortb2Fragments.bidder.ix.user.data).that.is.empty; + }); + + it('filterSDAwhenEID for All EID ', function () { + let dataControllerConfiguration = { + 'dataController': { + filterSDAwhenEID: ['*'] + } + }; + config.setConfig(dataControllerConfiguration); + + filterBidData(callbackFn, req); + expect(req.ortb2Fragments.bidder.ix.user.data).that.is.empty; + expect(req.ortb2Fragments.global.user.data).that.is.empty; + }); + + it('filterSDAwhenEID for unavailable source test-sync.com EID ', function () { + let dataControllerConfiguration = { + 'dataController': { + filterSDAwhenEID: ['test-sync.com'] + } + }; + config.setConfig(dataControllerConfiguration); + filterBidData(callbackFn, req); + expect(req.ortb2Fragments.bidder.ix.user.data).that.is.not.empty; + }); + } + ); +}); From e84e58c7072619ed9917960c800fa9397ac17638 Mon Sep 17 00:00:00 2001 From: Tachfine Date: Mon, 25 Jul 2022 18:07:22 +0200 Subject: [PATCH 066/569] Criteo - Implement user sync iframe (#8577) Co-authored-by: le.labat --- modules/criteoBidAdapter.js | 142 ++++++++++++++++ test/spec/modules/criteoBidAdapter_spec.js | 180 ++++++++++++++++++++- 2 files changed, 321 insertions(+), 1 deletion(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 70c4e15da04..fea77bbe462 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -6,6 +6,8 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { find } from '../src/polyfill.js'; import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; // ref#2 import { getStorageManager } from '../src/storageManager.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { hasPurpose1Consent } from '../src/utils/gpdr.js'; const GVLID = 91; export const ADAPTER_VERSION = 34; @@ -31,12 +33,111 @@ const PUBLISHER_TAG_URL_TEMPLATE = 'https://static.criteo.net/js/ld/publishertag const FAST_BID_PUBKEY_E = 65537; const FAST_BID_PUBKEY_N = 'ztQYwCE5BU7T9CDM5he6rKoabstXRmkzx54zFPZkWbK530dwtLBDeaWBMxHBUT55CYyboR/EZ4efghPi3CoNGfGWezpjko9P6p2EwGArtHEeS4slhu/SpSIFMjG6fdrpRoNuIAMhq1Z+Pr/+HOd1pThFKeGFr2/NhtAg+TXAzaU='; +const SID_COOKIE_NAME = 'cto_sid'; +const IDCPY_COOKIE_NAME = 'cto_idcpy'; +const LWID_COOKIE_NAME = 'cto_lwid'; +const OPTOUT_COOKIE_NAME = 'cto_optout'; +const BUNDLE_COOKIE_NAME = 'cto_bundle'; +const GUID_RETENTION_TIME_HOUR = 24 * 30 * 13; // 13 months +const OPTOUT_RETENTION_TIME_HOUR = 5 * 12 * 30 * 24; // 5 years + /** @type {BidderSpec} */ export const spec = { code: BIDDER_CODE, gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], + getUserSyncs: function (syncOptions, _, gdprConsent, uspConsent) { + const fastBidVersion = config.getConfig('criteo.fastBidVersion'); + if (canFastBid(fastBidVersion)) { + return []; + } + + const refererInfo = getRefererInfo(); + const origin = 'criteoPrebidAdapter'; + + if (syncOptions.iframeEnabled && hasPurpose1Consent(gdprConsent)) { + const queryParams = []; + queryParams.push(`origin=${origin}`); + queryParams.push(`topUrl=${refererInfo.domain}`); + if (gdprConsent) { + if (gdprConsent.gdprApplies) { + queryParams.push(`gdpr=${gdprConsent.gdprApplies == true ? 1 : 0}`); + } + if (gdprConsent.consentString) { + queryParams.push(`gdpr_consent=${gdprConsent.consentString}`); + } + } + if (uspConsent) { + queryParams.push(`us_privacy=${uspConsent}`); + } + + const requestId = Math.random().toString(); + + const jsonHash = { + bundle: readFromAllStorages(BUNDLE_COOKIE_NAME), + cw: storage.cookiesAreEnabled(), + localWebId: readFromAllStorages(LWID_COOKIE_NAME), + lsw: storage.localStorageIsEnabled(), + optoutCookie: readFromAllStorages(OPTOUT_COOKIE_NAME), + origin: origin, + requestId: requestId, + secureIdCookie: readFromAllStorages(SID_COOKIE_NAME), + tld: refererInfo.domain, + topUrl: refererInfo.domain, + uid: readFromAllStorages(IDCPY_COOKIE_NAME), + version: '$prebid.version$'.replace(/\./g, '_'), + }; + + window.addEventListener('message', function handler(event) { + if (!event.data || event.origin != 'https://gum.criteo.com') { + return; + } + + if (event.data.requestId !== requestId) { + return; + } + + this.removeEventListener('message', handler); + + event.stopImmediatePropagation(); + + const response = event.data; + + if (response.optout) { + deleteFromAllStorages(IDCPY_COOKIE_NAME); + deleteFromAllStorages(SID_COOKIE_NAME); + deleteFromAllStorages(BUNDLE_COOKIE_NAME); + deleteFromAllStorages(LWID_COOKIE_NAME); + + saveOnAllStorages(OPTOUT_COOKIE_NAME, true, OPTOUT_RETENTION_TIME_HOUR); + } else { + if (response.uid) { + saveOnAllStorages(IDCPY_COOKIE_NAME, response.uid, GUID_RETENTION_TIME_HOUR); + } + + if (response.bundle) { + saveOnAllStorages(BUNDLE_COOKIE_NAME, response.bundle, GUID_RETENTION_TIME_HOUR); + } + + if (response.removeSid) { + deleteFromAllStorages(SID_COOKIE_NAME); + } else if (response.sid) { + saveOnAllStorages(SID_COOKIE_NAME, response.sid, GUID_RETENTION_TIME_HOUR); + } + } + }, true); + + const jsonHashSerialized = JSON.stringify(jsonHash).replace(/"/g, '%22'); + + return [{ + type: 'iframe', + url: `https://gum.criteo.com/syncframe?${queryParams.join('&')}#${jsonHashSerialized}` + }]; + } + return []; + }, + /** f * @param {object} bid * @return {boolean} @@ -209,6 +310,27 @@ export const spec = { }, }; +function readFromAllStorages(name) { + const fromCookie = storage.getCookie(name); + const fromLocalStorage = storage.getDataFromLocalStorage(name); + + return fromCookie || fromLocalStorage || undefined; +} + +function saveOnAllStorages(name, value, expirationTimeHours) { + const date = new Date(); + date.setTime(date.getTime() + (expirationTimeHours * 60 * 60 * 1000)); + const expires = `expires=${date.toUTCString()}`; + + storage.setCookie(name, value, expires); + storage.setDataInLocalStorage(name, value); +} + +function deleteFromAllStorages(name) { + storage.setCookie(name, '', 0); + storage.removeDataFromLocalStorage(name); +} + /** * @return {boolean} */ @@ -271,6 +393,26 @@ function buildCdbUrl(context) { url += '&nolog=1'; } + const bundle = readFromAllStorages(BUNDLE_COOKIE_NAME); + if (bundle) { + url += `&bundle=${bundle}`; + } + + const optout = readFromAllStorages(OPTOUT_COOKIE_NAME); + if (optout) { + url += `&optout=1`; + } + + const sid = readFromAllStorages(SID_COOKIE_NAME); + if (sid) { + url += `&sid=${sid}`; + } + + const idcpy = readFromAllStorages(IDCPY_COOKIE_NAME); + if (idcpy) { + url += `&idcpy=${idcpy}`; + } + return url; } diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index c2f19fc3f78..58f8e1ad871 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -10,9 +10,10 @@ import { import { createBid } from 'src/bidfactory.js'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; +import * as refererDetection from 'src/refererDetection.js'; import { config } from '../../../src/config.js'; -import { NATIVE, VIDEO } from '../../../src/mediaTypes.js'; import * as storageManager from 'src/storageManager.js'; +import { NATIVE, VIDEO } from '../../../src/mediaTypes.js'; describe('The Criteo bidding adapter', function () { let utilsMock, sandbox; @@ -37,6 +38,183 @@ describe('The Criteo bidding adapter', function () { sandbox.restore(); }); + describe('getUserSyncs', function () { + const syncOptionsIframeEnabled = { + iframeEnabled: true + }; + + const expectedHash = { + cw: true, + lsw: true, + origin: 'criteoPrebidAdapter', + requestId: '123456', + tld: 'www.abc.com', + topUrl: 'www.abc.com', + version: '$prebid.version$'.replace(/\./g, '_'), + }; + + let randomStub, + getConfigStub, + getRefererInfoStub, + cookiesAreEnabledStub, + localStorageIsEnabledStub, + getCookieStub, + getDataFromLocalStorageStub; + + beforeEach(function () { + getConfigStub = sinon.stub(config, 'getConfig'); + getConfigStub.withArgs('criteo.fastBidVersion').returns('none'); + + randomStub = sinon.stub(Math, 'random'); + randomStub.returns(123456); + + getRefererInfoStub = sinon.stub(refererDetection, 'getRefererInfo'); + getRefererInfoStub.returns({ + domain: 'www.abc.com' + }); + + cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); + cookiesAreEnabledStub.returns(true); + localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); + localStorageIsEnabledStub.returns(true); + + getCookieStub = sinon.stub(storage, 'getCookie') + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + }); + + afterEach(function () { + randomStub.restore(); + getConfigStub.restore(); + getRefererInfoStub.restore(); + cookiesAreEnabledStub.restore(); + localStorageIsEnabledStub.restore(); + getCookieStub.restore(); + getDataFromLocalStorageStub.restore(); + }); + + it('should not trigger sync if publisher is using fast bid', function () { + getConfigStub.withArgs('criteo.fastBidVersion').returns('latest'); + + const userSyncs = spec.getUserSyncs(syncOptionsIframeEnabled, undefined, undefined, undefined); + + expect(userSyncs).to.eql([]); + }); + + it('should not trigger sync if publisher did not enable iframe based syncs', function () { + const userSyncs = spec.getUserSyncs({ + iframeEnabled: false + }, undefined, undefined, undefined); + + expect(userSyncs).to.eql([]); + }); + + it('should not trigger sync if purpose one is not granted', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'ABC', + vendorData: { + purpose: { + consents: { + 1: false + } + } + } + }; + const userSyncs = spec.getUserSyncs(syncOptionsIframeEnabled, undefined, gdprConsent, undefined); + + expect(userSyncs).to.eql([]); + }); + + it('forwards ids from cookies', function () { + const cookieData = { + 'cto_bundle': 'a', + 'cto_sid': 'b', + 'cto_lwid': 'c', + 'cto_idcpy': 'd', + 'cto_optout': 'e' + }; + + const expectedHashWithCookieData = { + ...expectedHash, + ...{ + bundle: cookieData['cto_bundle'], + localWebId: cookieData['cto_lwid'], + secureIdCookie: cookieData['cto_sid'], + uid: cookieData['cto_idcpy'], + optoutCookie: cookieData['cto_optout'] + } + }; + + getCookieStub.callsFake(cookieName => cookieData[cookieName]); + + const userSyncs = spec.getUserSyncs(syncOptionsIframeEnabled, undefined, undefined, undefined); + + expect(userSyncs).to.eql([{ + type: 'iframe', + url: `https://gum.criteo.com/syncframe?origin=criteoPrebidAdapter&topUrl=www.abc.com#${JSON.stringify(expectedHashWithCookieData, Object.keys(expectedHashWithCookieData).sort()).replace(/"/g, '%22')}` + }]); + }); + + it('forwards ids from local storage', function () { + const localStorageData = { + 'cto_bundle': 'a', + 'cto_sid': 'b', + 'cto_lwid': 'c', + 'cto_idcpy': 'd', + 'cto_optout': 'e' + }; + + const expectedHashWithLocalStorageData = { + ...expectedHash, + ...{ + bundle: localStorageData['cto_bundle'], + localWebId: localStorageData['cto_lwid'], + secureIdCookie: localStorageData['cto_sid'], + uid: localStorageData['cto_idcpy'], + optoutCookie: localStorageData['cto_optout'] + } + }; + + getDataFromLocalStorageStub.callsFake(localStorageName => localStorageData[localStorageName]); + + const userSyncs = spec.getUserSyncs(syncOptionsIframeEnabled, undefined, undefined, undefined); + + expect(userSyncs).to.eql([{ + type: 'iframe', + url: `https://gum.criteo.com/syncframe?origin=criteoPrebidAdapter&topUrl=www.abc.com#${JSON.stringify(expectedHashWithLocalStorageData, Object.keys(expectedHashWithLocalStorageData).sort()).replace(/"/g, '%22')}` + }]); + }); + + it('forwards gdpr data', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'ABC', + vendorData: { + purpose: { + consents: { + 1: true + } + } + } + }; + const userSyncs = spec.getUserSyncs(syncOptionsIframeEnabled, undefined, gdprConsent, undefined); + + expect(userSyncs).to.eql([{ + type: 'iframe', + url: `https://gum.criteo.com/syncframe?origin=criteoPrebidAdapter&topUrl=www.abc.com&gdpr=1&gdpr_consent=ABC#${JSON.stringify(expectedHash).replace(/"/g, '%22')}` + }]); + }); + + it('forwards usp data', function () { + const userSyncs = spec.getUserSyncs(syncOptionsIframeEnabled, undefined, undefined, 'ABC'); + + expect(userSyncs).to.eql([{ + type: 'iframe', + url: `https://gum.criteo.com/syncframe?origin=criteoPrebidAdapter&topUrl=www.abc.com&us_privacy=ABC#${JSON.stringify(expectedHash).replace(/"/g, '%22')}` + }]); + }); + }); + describe('isBidRequestValid', function () { it('should return false when given an invalid bid', function () { const bid = { From 8d0f10a23743d65005611c8a3fbecb885c31ca78 Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Mon, 25 Jul 2022 09:09:42 -0700 Subject: [PATCH 067/569] PBS Adapter: Support Bidder-Specific Schains (#8594) * implementation for issue 8561 * removed eslint disable comment * added tests * removed unecessary comments * addressed feedback * reverted a few changes that were used for dev on my local * addressed feedback and refactored code * reverted changes to the prebidServer_example file Co-authored-by: Jason Quaccia --- modules/prebidServerBidAdapter/index.js | 37 +++++ .../modules/prebidServerBidAdapter_spec.js | 157 ++++++++++++++++++ 2 files changed, 194 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 4007c67c82f..bc90fe149de 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -861,6 +861,43 @@ Object.assign(ORTB2.prototype, { request.ext.prebid = mergeDeep(request.ext.prebid, s2sConfig.extPrebid); } + // get reference to pbs config schain bidder names (if any exist) + const pbsSchainBidderNamesArr = request.ext.prebid?.schains ? request.ext.prebid.schains.flatMap(s => s.bidders) : []; + // create an schains object + const schains = Object.fromEntries( + (request.ext.prebid?.schains || []).map(({bidders, schain}) => [JSON.stringify(schain), {bidders: new Set(bidders), schain}]) + ); + + // compare bidder specific schains with pbs specific schains + request.ext.prebid.schains = Object.values( + bidRequests + .map((req) => [req.bidderCode, req.bids[0].schain]) + .reduce((chains, [bidder, chain]) => { + const chainKey = JSON.stringify(chain); + + switch (true) { + // if pbjs bidder name is same as pbs bidder name, pbs bidder name always wins + case chainKey && pbsSchainBidderNamesArr.indexOf(bidder) !== -1: + logInfo(`bidder-specific schain for ${bidder} skipped due to existing entry`); + break; + // if a pbjs schain obj is equal to an schain obj that exists on the pbs side, add the bidder name on the pbs side + case chainKey && chains.hasOwnProperty(chainKey) && pbsSchainBidderNamesArr.indexOf(bidder) === -1: + chains[chainKey].bidders.add(bidder); + break; + // if a pbjs schain obj is not on the pbs side, add a new schain entry on the pbs side + case chainKey && !chains.hasOwnProperty(chainKey): + chains[chainKey] = {bidders: new Set(), schain: chain}; + chains[chainKey].bidders.add(bidder); + break; + default: + } + + return chains; + }, schains) + ).map(({bidders, schain}) => ({bidders: Array.from(bidders), schain})); + // if schains evaluates to an empty array, remove it from the prebid object + if (request.ext.prebid.schains.length === 0) delete request.ext.prebid.schains; + /** * @type {(string[]|string|undefined)} - OpenRTB property 'cur', currencies available for bids */ diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 1e81abda57e..b1bbb02a115 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1875,6 +1875,163 @@ describe('S2S Adapter', function () { }); }); + it('should have extPrebid.schains present on req object if bidder specific schains were configured with pbjs', function () { + let bidRequest = utils.deepClone(BID_REQUESTS); + bidRequest[0].bids[0].schain = { + complete: 1, + nodes: [{ + asi: 'test.com', + hp: 1, + sid: '11111' + }], + ver: '1.0' + }; + + adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.ext.prebid.schains).to.deep.equal([ + { + bidders: ['appnexus'], + schain: { + complete: 1, + nodes: [ + { + asi: 'test.com', + hp: 1, + sid: '11111' + } + ], + ver: '1.0' + } + } + ]); + }); + + it('should skip over adding any bid specific schain entries that already exist on extPrebid.schains', function () { + let bidRequest = utils.deepClone(BID_REQUESTS); + bidRequest[0].bids[0].schain = { + complete: 1, + nodes: [{ + asi: 'pbjs.com', + hp: 1, + sid: '22222' + }], + ver: '1.0' + }; + + const s2sConfig = Object.assign({}, CONFIG, { + extPrebid: { + schains: [ + { + bidders: ['appnexus'], + schain: { + complete: 1, + nodes: [ + { + asi: 'pbs.com', + hp: 1, + sid: '11111' + } + ], + ver: '1.0' + } + } + ] + } + }); + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = s2sConfig; + + adapter.callBids(s2sBidRequest, bidRequest, addBidResponse, done, ajax); + + let requestBid = JSON.parse(server.requests[0].requestBody); + expect(requestBid.ext.prebid.schains).to.deep.equal([ + { + bidders: ['appnexus'], + schain: { + complete: 1, + nodes: [ + { + asi: 'pbs.com', + hp: 1, + sid: '11111' + } + ], + ver: '1.0' + } + } + ]); + }); + + it('should add a bidder name to pbs schain if the schain is equal to a pbjs one but the pbjs bidder name is not in the bidder array on the pbs side', function () { + let bidRequest = utils.deepClone(BID_REQUESTS); + bidRequest[0].bids[0].schain = { + complete: 1, + nodes: [{ + asi: 'test.com', + hp: 1, + sid: '11111' + }], + ver: '1.0' + }; + + bidRequest[0].bids[1] = { + bidder: 'rubicon', + params: { + accountId: 14062, + siteId: 70608, + zoneId: 498816 + } + }; + + const s2sConfig = Object.assign({}, CONFIG, { + bidders: ['rubicon', 'appnexus'], + extPrebid: { + schains: [ + { + bidders: ['rubicon'], + schain: { + complete: 1, + nodes: [ + { + asi: 'test.com', + hp: 1, + sid: '11111' + } + ], + ver: '1.0' + } + } + ] + } + }); + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = s2sConfig; + + adapter.callBids(s2sBidRequest, bidRequest, addBidResponse, done, ajax); + + let requestBid = JSON.parse(server.requests[0].requestBody); + expect(requestBid.ext.prebid.schains).to.deep.equal([ + { + bidders: ['rubicon', 'appnexus'], + schain: { + complete: 1, + nodes: [ + { + asi: 'test.com', + hp: 1, + sid: '11111' + } + ], + ver: '1.0' + } + } + ]); + }); + it('passes schain object in request', function () { const bidRequests = utils.deepClone(BID_REQUESTS); const schainObject = { From 252d9d4f82068f452dc9de6f3ac816190d61b4dd Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 25 Jul 2022 09:48:03 -0700 Subject: [PATCH 068/569] Prebid core: fix adUnits for auctoin at the time requestBids is called (#8637) --- src/hook.js | 11 ++++++++ src/prebid.js | 36 ++++++++++++++++---------- test/spec/unit/pbjs_api_spec.js | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/hook.js b/src/hook.js index 226c49daeae..3f01114935d 100644 --- a/src/hook.js +++ b/src/hook.js @@ -48,3 +48,14 @@ export function submodule(name, ...args) { next(modules); }); } + +/** + * Copy hook methods (.before, .after, etc) from a given hook to a given wrapper object. + */ +export function wrapHook(hook, wrapper) { + Object.defineProperties( + wrapper, + Object.fromEntries(['before', 'after', 'getHooks', 'removeAll'].map((m) => [m, {get: () => hook[m]}])) + ); + return wrapper; +} diff --git a/src/prebid.js b/src/prebid.js index ab336af08b6..a87c5a27f70 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -12,7 +12,7 @@ import { userSync } from './userSync.js'; import { config } from './config.js'; import { auctionManager } from './auctionManager.js'; import { filters, targeting } from './targeting.js'; -import { hook } from './hook.js'; +import {hook, wrapHook} from './hook.js'; import { loadSession } from './debugging.js'; import {includes} from './polyfill.js'; import { adunitCounter } from './adUnits.js'; @@ -573,18 +573,28 @@ $$PREBID_GLOBAL$$.removeAdUnit = function (adUnitCode) { * @param {String} requestOptions.auctionId * @alias module:pbjs.requestBids */ -$$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeout, adUnits, adUnitCodes, labels, auctionId, ortb2 } = {}) { - events.emit(REQUEST_BIDS); - const cbTimeout = timeout || config.getConfig('bidderTimeout'); - adUnits = adUnits || $$PREBID_GLOBAL$$.adUnits; - adUnits = (isArray(adUnits) ? adUnits : [adUnits]); - logInfo('Invoking $$PREBID_GLOBAL$$.requestBids', arguments); - const ortb2Fragments = { - global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), - bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, cfg.ortb2]).filter(([_, ortb2]) => ortb2 != null)) - } - return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ortb2Fragments}); -}, 'requestBids'); +$$PREBID_GLOBAL$$.requestBids = (function() { + const delegate = hook('async', function ({ bidsBackHandler, timeout, adUnits, adUnitCodes, labels, auctionId, ortb2 } = {}) { + events.emit(REQUEST_BIDS); + const cbTimeout = timeout || config.getConfig('bidderTimeout'); + logInfo('Invoking $$PREBID_GLOBAL$$.requestBids', arguments); + const ortb2Fragments = { + global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), + bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, cfg.ortb2]).filter(([_, ortb2]) => ortb2 != null)) + } + return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ortb2Fragments}); + }, 'requestBids'); + + return wrapHook(delegate, function requestBids(req = {}) { + // if the request does not specify adUnits, clone the global adUnit array - before + // any hook has a chance to run. + // otherwise, if the caller goes on to use addAdUnits/removeAdUnits, any asynchronous logic + // in any hook might see their effects. + let adUnits = req.adUnits || $$PREBID_GLOBAL$$.adUnits; + req.adUnits = (isArray(adUnits) ? adUnits.slice() : [adUnits]); + return delegate.call(this, req); + }); +})(); export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ortb2Fragments } = {}) { const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index ca646743147..5c0391f96bf 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -218,6 +218,51 @@ describe('Unit: Prebid Module', function () { auctionManager.clearAllAuctions(); }); + describe('and global adUnits', () => { + const startingAdUnits = [ + { + code: 'one', + }, + { + code: 'two', + } + ]; + let actualAdUnits, hookRan, done; + + function deferringHook(next, req) { + setTimeout(() => { + actualAdUnits = req.adUnits || $$PREBID_GLOBAL$$.adUnits; + done(); + }); + } + + beforeEach(() => { + $$PREBID_GLOBAL$$.requestBids.before(deferringHook, 99); + $$PREBID_GLOBAL$$.adUnits.splice(0, $$PREBID_GLOBAL$$.adUnits.length, ...startingAdUnits); + hookRan = new Promise((resolve) => { + done = resolve; + }); + }); + + afterEach(() => { + $$PREBID_GLOBAL$$.requestBids.getHooks({hook: deferringHook}).remove(); + $$PREBID_GLOBAL$$.adUnits.splice(0, $$PREBID_GLOBAL$$.adUnits.length); + }) + + Object.entries({ + 'addAdUnits': (g) => g.addAdUnits({code: 'three'}), + 'removeAdUnit': (g) => g.removeAdUnit('one') + }).forEach(([method, op]) => { + it(`once called, should not be affected by ${method}`, () => { + $$PREBID_GLOBAL$$.requestBids({}); + op($$PREBID_GLOBAL$$); + return hookRan.then(() => { + expect(actualAdUnits).to.eql(startingAdUnits); + }) + }); + }); + }); + describe('getAdserverTargetingForAdUnitCodeStr', function () { beforeEach(function () { resetAuction(); From 51dd9bd8fae4221197e58383939a5da2cb14bc1b Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Mon, 25 Jul 2022 09:57:04 -0700 Subject: [PATCH 069/569] Concert Bid Adapter: adding referer (Resubmit of Pr #8580) (#8676) * Concert Bid Adapter refererInfo update * fix linting * update with purpose1consent --- modules/concertBidAdapter.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js index 398248bfeab..fc6fc23c26d 100644 --- a/modules/concertBidAdapter.js +++ b/modules/concertBidAdapter.js @@ -1,6 +1,7 @@ import { logWarn, logMessage, debugTurnedOn, generateUUID } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { getStorageManager } from '../src/storageManager.js' +import { getStorageManager } from '../src/storageManager.js'; +import { hasPurpose1Consent } from '../src/utils/gpdr.js'; const BIDDER_CODE = 'concert'; const CONCERT_ENDPOINT = 'https://bids.concert.io'; @@ -45,7 +46,7 @@ export const spec = { uspConsent: bidderRequest.uspConsent, gdprConsent: bidderRequest.gdprConsent } - } + }; payload.slots = validBidRequests.map(bidRequest => { let slot = { @@ -57,8 +58,9 @@ export const spec = { slotType: bidRequest.params.slotType, adSlot: bidRequest.params.slot || bidRequest.adUnitCode, placementId: bidRequest.params.placementId || '', - site: bidRequest.params.site || bidderRequest.refererInfo.page - } + site: bidRequest.params.site || bidderRequest.refererInfo.page, + ref: bidderRequest.refererInfo.ref + }; return slot; }); @@ -69,7 +71,7 @@ export const spec = { method: 'POST', url: `${CONCERT_ENDPOINT}/bids/prebid`, data: JSON.stringify(payload) - } + }; }, /** * Unpack the response from the server into a list of bids. @@ -101,7 +103,7 @@ export const spec = { creativeId: bid.creativeId, netRevenue: bid.netRevenue, currency: bid.currency - } + }; }); if (debugTurnedOn() && serverBody.debug) { @@ -122,7 +124,7 @@ export const spec = { * @return {UserSync[]} The user syncs which should be dropped. */ getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = [] + const syncs = []; if (syncOptions.iframeEnabled && !hasOptedOutOfPersonalization()) { let params = []; @@ -203,9 +205,14 @@ function hasOptedOutOfPersonalization() { * @param {BidderRequest} bidderRequest Object which contains any data consent signals */ function consentAllowsPpid(bidderRequest) { - /* NOTE: We cannot easily test GDPR consent, without the + /* NOTE: We can't easily test GDPR consent, without the * `consent-string` npm module; so will have to rely on that * happening on the bid-server. */ - return !(bidderRequest.uspConsent === 'string' && - bidderRequest.uspConsent.toUpperCase().substring(0, 2) === '1YY') + const uspConsent = !(bidderRequest?.uspConsent === 'string' && + bidderRequest?.uspConsent[0] === '1' && + bidderRequest?.uspConsent[2].toUpperCase() === 'Y'); + + const gdprConsent = bidderRequest?.gdprConsent && hasPurpose1Consent(bidderRequest?.gdprConsent); + + return (uspConsent || gdprConsent); } From 599a8ddb23a87ac0dfd1af47e90d028e2d4bf4a9 Mon Sep 17 00:00:00 2001 From: Philip Watson Date: Tue, 26 Jul 2022 05:00:51 +1200 Subject: [PATCH 070/569] StroeerCore Bid Adapter: use page & ref from refererInfo, add schain support (#8705) --- modules/stroeerCoreBidAdapter.js | 13 ++----- .../modules/stroeerCoreBidAdapter_spec.js | 36 +++++++++++++++---- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js index 614dbdf6ef6..8e617eb24f8 100644 --- a/modules/stroeerCoreBidAdapter.js +++ b/modules/stroeerCoreBidAdapter.js @@ -13,14 +13,6 @@ const USER_SYNC_IFRAME_URL = 'https://js.adscale.de/pbsync.html'; const isSecureWindow = () => getWindowSelf().location.protocol === 'https:'; const isMainPageAccessible = () => getMostAccessibleTopWindow() === getWindowTop(); -function getTopWindowReferrer() { - try { - return getWindowTop().document.referrer; - } catch (e) { - return ''; - } -} - function getMostAccessibleTopWindow() { let res = getWindowSelf(); @@ -124,11 +116,12 @@ export const spec = { const payload = { id: bidderRequest.auctionId, bids: [], - ref: getTopWindowReferrer(), + ref: refererInfo.ref, ssl: isSecureWindow(), mpa: isMainPageAccessible(), timeout: bidderRequest.timeout - (Date.now() - bidderRequest.auctionStart), - url: refererInfo && (refererInfo.canonicalUrl || refererInfo.referer) + url: refererInfo.page, + schain: anyBid.schain }; const userIds = anyBid.userId; diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index 625bb4ac6b7..5b43b78280b 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -59,7 +59,8 @@ describe('stroeerCore bid adapter', function () { timeout: 5000, auctionStart: 10000, refererInfo: { - referer: 'https://www.example.com/index.html' + page: 'https://www.example.com/monkey/index.html', + ref: 'https://www.example.com/?search=monkey' }, bids: [{ bidId: 'bid1', @@ -109,7 +110,7 @@ describe('stroeerCore bid adapter', function () { }); const createWindow = (href, params = {}) => { - let {parent, referrer, top, frameElement, placementElements = []} = params; + let {parent, top, frameElement, placementElements = []} = params; const protocol = (href.indexOf('https') === 0) ? 'https:' : 'http:'; const win = { @@ -126,7 +127,6 @@ describe('stroeerCore bid adapter', function () { } } }, - referrer, getElementById: id => find(placementElements, el => el.id === id) } }; @@ -169,7 +169,7 @@ describe('stroeerCore bid adapter', function () { } function setupNestedWindows(sandBox, placementElements = [createElement('div-1', 17), createElement('div-2', 54)]) { - const topWin = createWindow('http://www.abc.org/', {referrer: 'http://www.google.com/?query=monkey'}); + const topWin = createWindow('http://www.abc.org/'); topWin.innerHeight = 800; const midWin = createWindow('http://www.abc.org/', {parent: topWin, top: topWin, frameElement: createElement()}); @@ -310,10 +310,10 @@ describe('stroeerCore bid adapter', function () { const expectedJsonPayload = { 'id': AUCTION_ID, 'timeout': expectedTimeout, - 'ref': topWin.document.referrer, + 'ref': 'https://www.example.com/?search=monkey', 'mpa': true, 'ssl': false, - 'url': 'https://www.example.com/index.html', + 'url': 'https://www.example.com/monkey/index.html', 'bids': [{ 'sid': 'NDA=', 'bid': 'bid1', 'siz': [[300, 600], [160, 60]], 'viz': true }, { @@ -403,6 +403,30 @@ describe('stroeerCore bid adapter', function () { assert.lengthOf(serverRequestInfo.data.bids, 2); assert.notProperty(serverRequestInfo, 'uids'); }); + + it('should add schain if available', () => { + const schain = Object.freeze({ + ver: '1.0', + complete: 1, + 'nodes': [ + { + asi: 'exchange1.com', + sid: 'ABC', + hp: 1, + rid: 'bid-request-1', + name: 'publisher', + domain: 'publisher.com' + } + ] + }); + + const bidReq = buildBidderRequest(); + bidReq.bids.forEach(bid => bid.schain = schain); + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + assert.deepEqual(serverRequestInfo.data.schain, schain); + }); }); }); }); From 37eabf5a73693affdeda7d34d2a3f1a3e3d00936 Mon Sep 17 00:00:00 2001 From: yieldlift <61510052+yieldlift@users.noreply.github.com> Date: Mon, 25 Jul 2022 19:01:52 +0200 Subject: [PATCH 071/569] Yieldlift Bid Adapter: add support for eids (#8707) * Added eids parameter * Added eids parameter test * Fixing utils Co-authored-by: Danijel Predarski --- modules/yieldliftBidAdapter.js | 8 +++++++- test/spec/modules/yieldliftBidAdapter_spec.js | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/yieldliftBidAdapter.js b/modules/yieldliftBidAdapter.js index fddc9a0d50b..160d7e9a009 100644 --- a/modules/yieldliftBidAdapter.js +++ b/modules/yieldliftBidAdapter.js @@ -1,4 +1,4 @@ -import { deepSetValue, logInfo, deepAccess } from '../src/utils.js'; +import {deepSetValue, logInfo, deepAccess} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; @@ -72,6 +72,12 @@ export const spec = { deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + // EIDS + const eids = deepAccess(validBidRequests[0], 'userIdAsEids'); + if (Array.isArray(eids) && eids.length > 0) { + deepSetValue(openrtbRequest, 'user.ext.eids', eids); + } + const payloadString = JSON.stringify(openrtbRequest); return { method: 'POST', diff --git a/test/spec/modules/yieldliftBidAdapter_spec.js b/test/spec/modules/yieldliftBidAdapter_spec.js index abb5a868c77..c2379ed7778 100644 --- a/test/spec/modules/yieldliftBidAdapter_spec.js +++ b/test/spec/modules/yieldliftBidAdapter_spec.js @@ -191,6 +191,26 @@ describe('YieldLift', function () { expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); expect(payload.regs.ext).to.have.property('gdpr', 1); }); + + it('should properly forward eids parameters', function () { + const req = Object.assign({}, REQUEST); + req.bidRequest[0].userIdAsEids = [ + { + source: 'dummy.com', + uids: [ + { + id: 'd6d0a86c-20c6-4410-a47b-5cba383a698a', + atype: 1 + } + ] + }]; + let request = spec.buildRequests(req.bidRequest, req); + + const payload = JSON.parse(request.data); + expect(payload.user.ext.eids[0].source).to.equal('dummy.com'); + expect(payload.user.ext.eids[0].uids[0].id).to.equal('d6d0a86c-20c6-4410-a47b-5cba383a698a'); + expect(payload.user.ext.eids[0].uids[0].atype).to.equal(1); + }); }); describe('interpretResponse', function () { From 18306dcfba68dff1b8d8bbb659887c898be7c761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Mon, 25 Jul 2022 20:03:59 +0300 Subject: [PATCH 072/569] Vidazoo Bid Adapter: send page query params to server (#8715) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server Co-authored-by: roman --- modules/vidazooBidAdapter.js | 14 ++++++++++++-- test/spec/modules/vidazooBidAdapter_spec.js | 10 ++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 3933d7632b6..7e2000e455a 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,4 +1,4 @@ -import { _each, deepAccess, parseSizesInput } from '../src/utils.js'; +import { _each, deepAccess, parseSizesInput, parseUrl } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -23,7 +23,16 @@ export const SUPPORTED_ID_SYSTEMS = { 'pubcid': 1, 'tdid': 1, }; -const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ gvlid: GVLID, bidderCode: BIDDER_CODE }); + +function getTopWindowQueryParams() { + try { + const parsedUrl = parseUrl(window.top.document.URL, { decodeSearchAsString: true }); + return parsedUrl.search; + } catch (e) { + return ''; + } +} export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.cootlogix.com`; @@ -59,6 +68,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { let data = { url: encodeURIComponent(topWindowUrl), + uqs: getTopWindowQueryParams(), cb: Date.now(), bidFloor: bidFloor, bidId: bidId, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 9e61768264f..dc178a9c485 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -81,6 +81,15 @@ const REQUEST = { } }; +function getTopWindowQueryParams() { + try { + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); + return parsedUrl.search; + } catch (e) { + return ''; + } +} + describe('VidazooBidAdapter', function () { describe('validtae spec', function () { it('exists and is a function', function () { @@ -171,6 +180,7 @@ describe('VidazooBidAdapter', function () { prebidVersion: version, schain: BID.schain, res: `${window.top.screen.width}x${window.top.screen.height}`, + uqs: getTopWindowQueryParams(), 'ext.param1': 'loremipsum', 'ext.param2': 'dolorsitamet', } From 852d0eeafe930f249cd85760b9d31d4c03b166b1 Mon Sep 17 00:00:00 2001 From: Bill Newman Date: Mon, 25 Jul 2022 20:12:08 +0300 Subject: [PATCH 073/569] Colossusssp Bid Adapter: fix traffic param (#8724) * add video&native traffic colossus ssp * Native obj validation * Native obj validation #2 * Added size field in requests * fixed test * fix merge conflicts * move to 3.0 * move to 3.0 * fix IE11 new URL issue * fix IE11 new URL issue * fix IE11 new URL issue * https for 3.0 * add https test * add ccp and schain features * fix test * sync with upstream, fix conflicts * Update colossussspBidAdapter.js remove commented code * Update colossussspBidAdapter.js lint fix * identity extensions * identity extensions * fix * fix * fix * fix * fix * add tests for user ids * fix * fix * fix * fix * fix * fix * fix * add gdpr support * add gdpr support * id5id support * Update colossussspBidAdapter.js add bidfloor parameter * Update colossussspBidAdapter.js check bidfloor * Update colossussspBidAdapter.js * Update colossussspBidAdapter.js * Update colossussspBidAdapter.js * Update colossussspBidAdapter_spec.js * use floor module * Revert "use floor module" This reverts commit f0c5c248627567e669d8eed4f2bb9a26a857e2ad. * use floor module * update to 5v * fix * add uid2 and bidFloor support * fix * add pbadslot support * fix conflicts * add onBidWon * refactor * add test for onBidWon() * fix * add group_id * Trigger circleci * fix * update user sync * fix window.location * fix test * updates * fix conflict * fix * updates * remove traffic param Co-authored-by: Vladislav Isaiko Co-authored-by: Aiholkin Co-authored-by: Mykhailo Yaremchuk --- modules/colossussspBidAdapter.js | 50 +++++++++++-------- modules/colossussspBidAdapter.md | 35 ++++++++++--- .../modules/colossussspBidAdapter_spec.js | 1 - 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index cd0721fa80f..eb6215765e1 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -108,12 +108,11 @@ export const spec = { for (let i = 0; i < validBidRequests.length; i++) { let bid = validBidRequests[i]; - let traff = bid.params.traffic || BANNER; + const { mediaTypes } = bid; let placement = { placementId: bid.params.placement_id, groupId: bid.params.group_id, bidId: bid.bidId, - traffic: traff, eids: [], floor: {} }; @@ -134,32 +133,39 @@ export const spec = { rtiPartner: 'TDID' }); } - if (traff === BANNER) { - placement.sizes = bid.mediaTypes[BANNER].sizes - } else if (traff === VIDEO) { - placement.sizes = bid.mediaTypes[VIDEO].playerSize; - placement.playerSize = bid.mediaTypes[VIDEO].playerSize; - placement.minduration = bid.mediaTypes[VIDEO].minduration; - placement.maxduration = bid.mediaTypes[VIDEO].maxduration; - placement.mimes = bid.mediaTypes[VIDEO].mimes; - placement.protocols = bid.mediaTypes[VIDEO].protocols; - placement.startdelay = bid.mediaTypes[VIDEO].startdelay; - placement.placement = bid.mediaTypes[VIDEO].placement; - placement.skip = bid.mediaTypes[VIDEO].skip; - placement.skipafter = bid.mediaTypes[VIDEO].skipafter; - placement.minbitrate = bid.mediaTypes[VIDEO].minbitrate; - placement.maxbitrate = bid.mediaTypes[VIDEO].maxbitrate; - placement.delivery = bid.mediaTypes[VIDEO].delivery; - placement.playbackmethod = bid.mediaTypes[VIDEO].playbackmethod; - placement.api = bid.mediaTypes[VIDEO].api; - placement.linearity = bid.mediaTypes[VIDEO].linearity; + + if (mediaTypes && mediaTypes[BANNER]) { + placement.traffic = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.traffic = VIDEO; + placement.sizes = mediaTypes[VIDEO].playerSize; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.traffic = NATIVE; + placement.native = mediaTypes[NATIVE]; } + if (typeof bid.getFloor === 'function') { let tmpFloor = {}; for (let size of placement.sizes) { tmpFloor = bid.getFloor({ currency: 'USD', - mediaType: traff, + mediaType: placement.traffic, size: size }); if (tmpFloor) { diff --git a/modules/colossussspBidAdapter.md b/modules/colossussspBidAdapter.md index 4187dfbf36e..45af89580c1 100644 --- a/modules/colossussspBidAdapter.md +++ b/modules/colossussspBidAdapter.md @@ -22,22 +22,45 @@ Module that connects to Colossus SSP demand sources bids: [{ bidder: 'colossusssp', params: { - placement_id: 0, - traffic: 'banner' + placement_id: 0 } }] }, { code: 'placementid_1', mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]] + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, } }, bids: [{ bidder: 'colossusssp', params: { - group_id: 0, - traffic: 'banner' + group_id: 0 + } + }] + }, { + code: 'placementid_2', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [{ + bidder: 'colossusssp', + params: { + placement_id: 0, } }] }]; diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index da050b3af90..f32a0bf4ebe 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -118,7 +118,6 @@ describe('ColossussspAdapter', function () { ...bid, params: { placement_id: 0, - traffic: 'video', }, mediaTypes: { video: { From 4bdad207cb8fa579930031d8b75be065f2e170c1 Mon Sep 17 00:00:00 2001 From: Federico Bartolomei Date: Tue, 26 Jul 2022 14:11:56 +0100 Subject: [PATCH 074/569] Permutive rtd module: update dev docs (#8736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Pull upstream (#24) * BidWatch Analytics Adapter: add new analytics adapter (#8302) * New Analytics Adapter bidwatch * test for bidwatch Analytics Adapter * change maintainer address * Update bidwatchAnalyticsAdapter.js * Update bidwatchAnalyticsAdapter.js * Update bidwatchAnalyticsAdapter.md * Update bidwatchAnalyticsAdapter.md * Native ads: change asset param (#8371) * TPMN Bidder Adapter: write id in first party domain; force syncs with various parties (#8341) * add TPMN UserSync Bidder Adapter(Test Modify) Updating the source code that was forked in the past. make test case more. - add pb7 bidderSettings option - userSync fix. * fix indentation from CircleCI error report * fix indentation from CircleCI error report * fix indentation from CircleCI error report * fix user sync. static user sync without checking uuid. * fix user sync. static user sync without checking uuid. * fix user sync. static user sync without checking uuid. * fix user sync. static user sync without checking uuid. Co-authored-by: changjun * Jixie Bid Adapter: send device info (#8397) * Adapter does not seem capable of supporting advertiserDomains #6650 added response comment and some trivial code. * removed a blank line at the end of file added a space behind the // in comments * in response to comment from reviewer. add the aspect of advertiserdomain in unit tests * added the code to get the keywords from the meta tags if available. * to support sending object of device info to backend. yes the backend can handle it even though device was a string so far * LKQD Bid Adapter: remove device ip bug (#8400) * LKQD: remove device ip from request * LKQD: remove device IP test * cpex Id System: initial release (#8364) * Adds cpexIdSystem * Fixes cpexIdSystem tests * Added markdown document * Remove unnecessary storage config * UserID module: graceful handling of exceptions from ID submodules (#8401) * Do not error out when malformed JSON is set in cookies * UserID module: graceful handling of exceptions from ID submodules * removing floor field from bid request when not defined (#8398) * passing floors signal to PBS (#8392) * add adunit floor min price floors (#8396) * AirGrid RTD module: Support for xandr / appnexus auction level keywords. (#8388) * chore: update docs page to match official docs * feat: add support for appnexus / xandr auction level keywords * Alkimi Bid Adapter: change maintainer email (#8405) * Alkimi bid adapter * Alkimi bid adapter * Alkimi bid adapter * alkimi adapter * onBidWon change * sign utils * auction ID as bid request ID * unit test fixes * change maintainer info Co-authored-by: Alexander Bogdanov * TheMediaGrid: support ortb2.site.content.data (#8404) * PubMatic Bid Adapter : Remove GroupM as alias (#8386) * Changed net revenue to True * Removed groupm as alias * Pasing the alternateBidder values to translator Co-authored-by: Azhar * Intentiq module: adding dynamic ttl and rtt and storing data in localstorate (#8384) * Intentiq module: adding dynamic ttl and data to LS * Fixing Useless conditional alerts Co-authored-by: Julian * Floc ID Module: remove call for floc (#8402) * Update flocIdSystem.js * Update flocIdSystem.js * Multiple modules: replace all usage of `require` with `import` (#8379) We had multiple reports of an elusive issue that seems to be related to use of `require` (https://github.com/prebid/Prebid.js/issues/8374). Since mixed use of `require` and `import`s is not great anyway, this is a good opportunity to clean it up. * Sirdata RTD Module: bug fixes & add new bidders (#8406) * - bug fixes - new bidders supported: yahoo, pubmatic, openx * fix invalid indentation * Revert "fix invalid indentation" This reverts commit 6ed9f9116b86c484b1d3b895315a77e03651369d. * manually update indentation * AdagioBidAdapter: fix site information detection (use refererInfo) (#8407) * Criteo : Add support of PAF response model (#8359) * JwPlayer RTD Module: Set Ortb content to config (#8354) * sets to config * updates unit tests * clears obsolete data * typo * replaces duplicate strings * Audigent analytics adapter: add new analytics adapter (#8347) * adding logic and tests for hadronAnalyticsAdapter * increasing coverage * typo in partnerId procedence * Sspbc Bid Adapter: Gather language in payload; various formatting changes (#8395) * Update tests for sspBC adapter Update tests for sspBC adapter: - change userSync test (due to tcf param appended in v4.6) - add tests for onBidWon and onTimeout * [sspbc-adapter] 5.3 updates: content-type for notifications * [sspbc-adapter] pass CTA to native bid * [sspbc-5.3] keep pbsize for detected adunits * [sspbc-5.3] increment adaptor ver * [sspbc-adapter] maintenance update to sspBCBidAdapter * remove yarn.lock * Delete package-lock.json * remove package-lock.jsonfrom pull request * [sspbc-adapter] send pageViewId in request * [sspbc-adapter] update pageViewId test * [sspbc-adapter] add viewabiility tracker to native ads * [sspbc-adapter] add support for bid.admNative property * [sspbc-adapter] ensure that placement id length is always 3 (improves matching response to request) * [sspbc-adapter] read publisher id and custom ad label, then send them to banner creative * [sspbc-adapter] adlabel and pubid are set as empty strings, if not present in bid response * [sspbc-adapter] jstracker data fix * [sspbc-adapter] jstracker data fix * [sspbc-adapter] send tagid in notifications * [sspbc-adapter] add gvlid to spec; prepare getUserSyncs for iframe + image sync * [sspbc-adapter] fix notification payload * [sspbc-adapter] fix notification payload, fix tests * [sspbc-adapter] add userIds to ortb request * [sspbc-adapter] update to 4.1, change request to be ortb 2.6 compliant * [sspbc-adapter] update tests * [ssbc-adapter] bid cache for video ads * [sspbc-adapter] add PageView.id to banner ad; update tests * [sspbc-adapter] fix window.gam not being added to banner html * [sspbc-adapter] send device / content language * [sspbc-adapter] send pageview and site ids to user sync frame * [sspbc-adapter] add ES6 version of common ad library (for banner creatives) * [sspbc-adapter] move content property * [sspbc-adapter] reorganize notification payload creator * [sspbc-adapter] store PLN price in meta; send in bidWon notification * [sspbc-adapter] add playbackmethod to supporten video params; allow overridinbg video settngs via bid.params.video * [sspbc-adapter] update md * [sspbc-adapter] fix error in mapVideo method (Object assign merror when mediaTypes do not contain video) Co-authored-by: Wojciech Biały * Prebid 6.24.0 release * Increment version to 6.25.0-pre * fix(deps): declare required deps for webpack build (#8411) When using Prebid.js as a NPM dependency and built with webpack, the instructions are to use the babel configuration and plugins that are provided by Prebid. ``` { test: /.js$/, include: new RegExp(`\\${path.sep}prebid\\.js`), use: { loader: 'babel-loader', // presets and plugins for Prebid.js must be manually specified separate from your other babel rule. // this can be accomplished by requiring prebid's .babelrc.js file (requires Babel 7 and Node v8.9.0+) // as of Prebid 6, babelrc.js only targets modern browsers. One can change the targets and build for // older browsers if they prefer, but integration tests on ie11 were removed in Prebid.js 6.0 options: require('prebid.js/.babelrc.js') } } ``` This seems to work just fine when using NPM or Yarn 1. However it is not possible when using stricter settings or package managers that require strict dependency declarations. ``` [webpack-cli] Failed to load '/Users/john.wright/Workspace/mung-app/webpack.config.js' config [webpack-cli] Error: prebid.js tried to access @babel/preset-env, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound. ``` * Multiple modules: automatically fill in PPID for DFP video URLs (#8365) https://github.com/prebid/Prebid.js/issues/8151 * Prebid 6.24.1 release * Increment version to 6.25.0-pre * Invibes Bid Adapter: write id to first party from bid adapter (#8202) * Invibes Bid Adapter: added support for LiD generation on Invibes servers * Invibes Bid Adapter: replaced all cookies storing with local storage removed cookie storing logic * invibesBidAdapter: applied lint formatting updated unit tests to reflect reading from localStorage * Update invibesBidAdapter_spec.js updated Invibes Bid Adapter unit tests to pass for when the default storage allowed will be changed. * Weborama RTD submodule: specify list of bidders to share data (#8350) * update .submodules.json to include weborama rtd update .submodules.json to include weborama rtd submodule * add support to pubmatic * improve test format * improve test format ~ * add support to appnexus/xandr bidder * fix issue when split set target in two steps: site and user centric data * add support to rubicon old style * fix code, update docs * add support to global and bid ortb2 * remove unused code * refactor code * update bidder list * add global level parameters * change error to warning * update doc * refactor * add field accountId * update example * refactor js concat string * refactor js concat string 2 * update jsdoc * correct jsdoc * add support to pubmatic * improve test format * improve test format ~ * add support to appnexus/xandr bidder * fix issue when split set target in two steps: site and user centric data * add support to rubicon old style * fix code, update docs * add support to global and bid ortb2 * remove unused code * refactor code * update bidder list * add global level parameters * change error to warning * update doc * refactor * add field accountId * update example * refactor js concat string * refactor js concat string 2 * update jsdoc * correct jsdoc * fix log * update example * remove todo * refactor duplicated code in config normalization * fix jsdoc * add main feature * update jstag * improve callback * improve doc * improve doc about ortb2 * refactor code * refactor tests to use one adunit * fix unit tests * prepare to add support to webo lite * refactor code * reorder code to handle bid data * finish unit test * improve copy of data * improve unit test by checking callbacks that alter bid data * format source * fix doc * specify webo lite as site-centric data * add check for profile format * update doc and code * update sendToBidder callback signature * fix doc * fix doc 2 * fix js example * update doc * improve doc * fix doc for LiTE * update code and tests * update doc * update unit test * improve doc * fix sfbx lite code, add isDefault flag on metadata * remove unused imports in tests * refactor code using ?. operator * improve deep clone usage * keep code less dinamic * refactor thinking in the prebid 7 * format source * format tests * suppress mention to lite * Revert "suppress mention to lite" This reverts commit 43fa65804446c4a6acf18dd48897f971c5d85683. * small fix * refactor init submodule functions * add new constants * update example * simplify code * rename function, rewrite callbacks into () => notation * update sfbx lite name * fix doc * TrustP ID Module: Refactor acronyms handler (#8228) * feature: Add trustpid user id module * refactor: Update trustpidSystem module, comments and md file * refactor: Update trustpidSystem file to handle domain setting regardless of message events * refactor: Update trustpidSystem module and dependent tests and docs * refactor: Update trustpid undefined checks and returns * tests: Update trustpid tests * docs: Update trustpid docs typo * refactor: Update trustpid acronyms logic * refactor: Remove unused import * refactor: Trustpid documentation update Co-authored-by: Tomasz Januszek * appnexus bid adapter - fix in psp keywords logic (#8382) * UserID module: do not start initialization until `pbjs.processQueue()` has been called (#8408) * Extract controllable promise into utils function * Do not start init of userId until processQueue is called * Add PGAM client side (#8418) * IX Bid Adapter: Outstream Support Update (#8412) * outstream player update * documentation update Co-authored-by: Love Sharma * Alkimi Bid Adapter: Markdown file example update (#8422) * Alkimi bid adapter * Alkimi bid adapter * Alkimi bid adapter * alkimi adapter * onBidWon change * sign utils * auction ID as bid request ID * unit test fixes * change maintainer info * Updated the ad unit params Co-authored-by: Alexander <32703851+pro-nsk@users.noreply.github.com> Co-authored-by: Alexander Bogdanov Co-authored-by: Alexander Bogdanov Co-authored-by: motors * BeOp Bid Adapter: prefer canonical URL when present & prepend protocol (#8391) * [BeOp] prefer canonical URL when present & prepend protocol * [BeOp] get the right page url in tracking events too * [BeOp] improve page url resolver * BeOp Bid Adapter: 'top.location.protocol' can also throw * feat: add detected page url to ixdiag [PB-978] (#8425) Co-authored-by: shahin.rahbariasl * LiveIntent Id Submodule: Update live-connect to 2.3.3; better consent processing (#8423) * Update live-connect to 2.3.3 * Update package-lock.json * Fix tests * Adjust test cases Co-authored-by: Viktor Dreiling Co-authored-by: Viktor Dreiling * support cta and privacyLink (#8403) * Kargo Bid Adapter: adding media type to bid response, supporting vastXml response (#8426) * kargo adapter - adding mediaType to bid response, conditionally set vastXml field * kargo adapter - updating tests * Improve Digital adapter: adding Extend mode (#8399) Co-authored-by: iosfaisal <100519197+iosfaisal@users.noreply.github.com> Co-authored-by: Jozef Bartek Co-authored-by: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Co-authored-by: Faisal Islam <93644923+faisalvs@users.noreply.github.com> Co-authored-by: iosfaisal <100519197+iosfaisal@users.noreply.github.com> * Update sync URLs (#8431) We are updating sync URLs to our current partners. * AdagioBidAdapter: remove useless data in bid request. (#8434) * Prebid 6.25.0 release * Increment version to 6.26.0-pre * Ogury Bid Adapter: Add device infos with size in bidrequest (#8416) * Add device infos with size in bidrequest * trigger rebuild on CI to fix tests * restore sinon stubbed methods after all tests * restore stub correctly at end of tests * trigger rebuild on CI to fix tests * Video Heroes Bid Adapter: add new bid adapter (#8310) * added Brave bidder adapter with test and docs Commit has standard bidder adapter 2 new files adapter js, adapter md * added test spec file witch covered code least 80 % * adding videoHeroes bidder adapter * added videoHeroes test spec * Update braveBidAdapter.js * Update braveBidAdapter.js * Update videoheroesBidAdapter.md * upd import of utils lib to load only certain fnc * cpex Id module: Remove window reference (#8440) * Remove window reference * Changed version to string * Jixie Bid Adapter: add schain (#8439) * Adapter does not seem capable of supporting advertiserDomains #6650 added response comment and some trivial code. * removed a blank line at the end of file added a space behind the // in comments * in response to comment from reviewer. add the aspect of advertiserdomain in unit tests * added the code to get the keywords from the meta tags if available. * jixie sending schain info to jixie backend * support of schain info sent to jixie endpoint, fixing some indentation complaints from auto build check * Gravito User Id submodule: initial release (#8414) * gravitompId user module for integrating gravito first party cookie with prebid js * fixed eslint issues raised by circleci * fixed trailing spaces error raised by circleci * Rename id to aoneId from dacId (#8453) * Akamai RTD: fixed bugs on rtd module and added the entropy values (#8284) * Fixed bugs on rtd module and added the entropy values required by Akamai DAP * Fixed the timeout issue in build browserstack tests * Fixing review comments * Fixing review comments - using storage manager for managing localStorage * Fixing review comments - using loadExternalScript method to load the script * Fixed unit test case * Fixing review comments - Added consent handling * Zeta global ssp bid adapter: add shortname param (#8454) * zeta_global_sspBidAdapter shortname was added * remove the trash Co-authored-by: Surovenko Alexey * Remove medianetRtdProvider tests (#8463) * Revert "Akamai RTD: fixed bugs on rtd module and added the entropy values (#8284)" (#8464) This reverts commit c4348892b5d3425d21373e1dcf4d67da04fed622. * Prebid 6.26.0 release * Increment version to 6.27.0-pre * Update fintezaAnalyticsAdapter_spec.js (#8467) * Hadron analytics adapter: fix cross-origin exception on init (#8472) * Pulling the URL check out from the logic. Whatever the user passes will be used (#8473) Co-authored-by: Jason Lydon * Update conversant adapter to accept position from the AdUnit (#8477) * NovatiqId User Module - Include IAB Vendor ID (#8479) * Novatiq snowflake userId submodule Novatiq snowflake userId submodule initial release * change request updates added novatiq info /modules/userId/userId.md added novatiq info /modules/userId/eids.md added novatiq eids /modules/userId/eids.js added novatiq module in /modules/.submodules.json removed unnecessary value from getId response * Update novatiqIdSystem_spec.js removed unnecessary srcid value * Update novatiqIdSystem.md Novatiq ID System: updated novatiq snowflake ID description * use the sharedId if available and configured * updated docs * test changes * defensive code not required * Use the prebid storage manager instead of using native functions * doc changes * trailing spaces * Allow configuration of the sync URL and to allow callbacks for specific custom partner integrations * update documentation * attempt to fix firefox test timeout * include the AIB Vendor Id Co-authored-by: novatiq <79258366+novatiq@users.noreply.github.com> * Multiple modules: attempt to reduce test flakiness, improved logging for XHR mock race conditions (#8466) * Datablocks bid adapter: do not send metrics during tests * Log contents of XHR mock on test failure * Disable ajax for analytics adapters during tests * Do not assume that test setup did not fail * Update to move floors logic after placement.sizes are defined (#8476) * Adloox Analytics/RTD: prefer gpid over pbadslot (#8455) * Adloox Analytics: use CSS.escape when possible * Adloox Analytics/RTD: support GPID * Akamai RTD: fixed bugs on rtd module and added the entropy values (#8470) * Fixed bugs on rtd module and added the entropy values required by Akamai DAP * Fixed the timeout issue in build browserstack tests * Fixing review comments * Fixing review comments - using storage manager for managing localStorage * Fixing review comments - using loadExternalScript method to load the script * Fixed unit test case * Fixing review comments - Added consent handling * SOVRN Bid Adapter: refactor old test code (#8430) * feat: [EX-3265] format tests for prebid.js adapter * feat: [EX-3265] add tests for video media type * feat: [EX-3265] fix test for interpretResponse * feat: [EX-3265] fix test for interpretResponse * feat: [EX-3265] fix test for interpretResponse Co-authored-by: Maxim Pakhomov Co-authored-by: Maxim Pakhomov <70204615+maximpakhomov@users.noreply.github.com> * Tests: remove console.log (#8481) Co-authored-by: Surovenko Alexey * Kobler adapter: remove outdated parameters, simplify testing (#8445) * Removed position parameter. * Removed zip parameter. * Removed placementId parameter and make sizes required instead. * Updated price-related macros. * Fixed error when params is not provided. * Removed last occurrence of placementId. * Read currency.adServerCurrency as publisherCurrency. * Use DEV endpoint for testing. * Use config.pageUrl when test is set to true. * Added more details about page URL. * `config.pageUrl`. * Added a comment explaining why pageUrl is considered only when testing. * Fixed double quotes in tests. * NaveggId module: fixed regex used to get naveggId from LocalStorage (#8441) * fixed regex used to get naveggId from LocalStorage * added unit tests Co-authored-by: Jose * Taboola bid adapter: initial release (#8483) * create taboola adapter * create taboola adapter md * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * update the md * update the Maintainer email * * update MD page * refactor code for better readability * small fix in UT * * add privacy to the request builder * add relevant Ut * small fixes in UT * * code refactoring + add more accurate way to get page url and referer * add relevant Ut * small fixes in md * * code refactoring + gte user id * add relevant Ut * small fixes * * code refactoring + gte user id * add relevant Ut * small fixes * * update end point url * update UT * Update banner End point structure * small fixes + update epi url * remove the destruction from the bidResponse property * (update the unit tests) remove the destruction from the bidResponse property * fix tests * fix tests - run stubs on each test * rerun because of another adapter flaky test * rerun because of another adapter flaky test * fix cors issue, switch between height, width position * update badv, bcat to be based in the ortb2 to support prebid 7 new protocols + update Ut * retry run circleci * retry run circleci * pull from upstream update md (placement + pub ) * update badv, bcat UT * rerun build * rerun build * support storageAllowed restriction on unit tests for prebid 7 * add it also to the aftereach * add it also to the aftereach * Prebid 6.27.0 release * Increment version to 6.28.0-pre * Ftrack Id Module: replace native appendChild with Prebid's loadExternalScript (#8432) * JDB-533: replacing appendChild in the ftrack prebid module * JDB-533: working on switching over to AJAX/CDN * JDB-533: removing some commented out code * JDB-533: removing redundant conditions Co-authored-by: Jason Lydon * IncrementX Bid Adapter: Initial Release (#8316) * IncrementX Bid Adapter: vertoz adapter renamed to IncrementX * IncrementX Bid Adapter: vertoz adapter renamed to IncrementX - document updated * IncrementX Bid Adapter: vertoz adapter renamed to IncrementX - vertozBidAdapter.md removed Co-authored-by: Mohit Patil * added tests for medianet rtd without loading js (#8485) Co-authored-by: monis.q * Adyoulike Bidder Adapter - schain support (#8486) * add schain data to the bid request * add unit test with schain data * ZetaGlobalSsp BidAdapter: shortname to url (#8471) * ZetaGlobalSsp bid adapter: shortname param into url param * fix tests Co-authored-by: Surovenko Alexey * Akamai RTD Module: Fixed randomly failing test cases and updated the variable names (#8487) * Fixed the randomly failing test cases and updated the variable names * Improving code quality * Adriver ID system: fix spurious test failure (#8489) * Inskin bid adapter: use loadExternalScript utility instead of appendChild() to insert the ad tag (#8490) * TheMediaGrid: support bid.ortb2.site.content.id (#8492) * Added TheMediaGridNM Bid Adapter * Updated required params for TheMediaGridNM Bid Adapter * Update TheMediGridNM Bid Adapter * Fix tests for TheMediaGridNM Bid Adapter * Fixes after review for TheMediaGridNM Bid Adapter * Add support of multi-format in TheMediaGrid Bid Adapter * Update sync url for grid and gridNM Bid Adapters * TheMediaGrid Bid Adapter: added keywords adUnit parameter * Update TheMediaGrid Bid Adapter to support keywords from config * Implement new request format for TheMediaGrid Bid Adapter * Fix jwpseg params for TheMediaGrid Bid Adapter * Update unit tests for The Media Grid Bid Adapter * Fix typo in TheMediaGrid Bid Adapter * Added test for jwTargeting in TheMediaGrid Bid Adapter * The new request format was made by default in TheMediaGrid Bid Adapter * Update userId format in ad request for TheMediaGrid Bid Adapter * Added bidFloor parameter for TheMediaGrid Bid Adapter * Fix for review TheMediaGrid Bid Adapter * Support floorModule in TheMediaGrid Bid Adapter * Fix empty bidfloor for TheMediaGrid Bid Adapter * Some change to restart autotests * Fix userIds format for TheMediaGrid Bid Adapter * Remove digitrust userId from TheMediaGrid Bid Adapter * Protocols was added in video section in ad request for TheMediaGrid Bid Adapter * TheMediaGrid: fix trouble with alias using * TheMediaGridNM: fix trouble with alias * TheMediaGrid Bid Adapter: added support of PBAdSlot module * TheMediaGrid Bid Adapter: fix typo * GridNM Bid Adapter: use absent in params data from mediaTypes * GridNM Bid Adapter: fix md file + add advertiserDomains support * TheMediaGrid and gridNM Bid Adapter: minor netRevenue fixes * gridNM Bid Adapter updates after review * TheMediaGrid Bid Adapter: fix keywords workflow * fix testing and kick off lgtm again * TheMediaGrid: added ext.bidder.grid.demandSource processing * TheMediaGrid: added user.id from fpd cookie * TheMediaGrid: control cookie setting via bidder config * TheMediaGrid: use localStorage instead cookie * TheMediaGridNM Bid Adapter: update adapter to use /hbjson endpoint * TheMediaGridNM: fix unnecessary conditions * TheMediaGrid: fix bug with nurl field in response * TheMediaGrid: update test * TheMediaGridNM: fix possible bug with nurl * TheMediaGrid: added alias as playwire * TheMediaGrid: added alias as adlivetech * TheMediaGrid: fix sync url workflow * TrustX: fix sync url worflow + remove old syncurl * TheMediaGrid: added instl support * TheMediaGrid: fix test for instl * TheMediaGrid: update md file * TheMediaGrid: reformat segments for permutive rtd module * TrustX: send all ortb2.user.data in user.data * TheMediaGrid: remove permutive segments reformating * TrustX: remove permutive segments reformating * TheMediaGrid & TrustX: fix typo * TheMediaGrid: support bid.ortb2.site.content * TheMediaGrid Bid Adapter: support bid.ortb2.site.content.id Co-authored-by: Chris Huie * Fix for #8421 (#8493) * Sending no decode whole url options to true, to avoid errors while decoding publisher url (#8497) * Automatad Bid Adapter: changes to ajax request options for bid requests (#8444) * changes for ajax request options for automatad bid request * add test for isCredentials changes * Various Magnite Adapters: GVL ID update (#8501) * Magnite adapters still require legacy GVL IDs * CTV update * Adf adapter: avoid preflight request (#8498) * Hadron RTD module: use internal methods (#8496) * following prebid team's advise * adding to adloader.js * removing generic import * using internal methods for loading external scripts in medianetRtdProvider (#8506) Co-authored-by: monis.q * Criteo - Update Publisher Tag version referenced by prebid adapter (#8491) * Kargo Bid Adapter: onTimeout Support (#8449) * Kargo Bid Adapter: Use currency from Bid Response * Kargo Bid Adapter: Fix failed test * Kargo Bid Adapter: adding media type to bid response, supporting vastXml response (#8426) * kargo adapter - adding mediaType to bid response, conditionally set vastXml field * kargo adapter - updating tests * Kargo Bid Adapter: onTimeout Support (#6) * Adding additional param * Adding response time function * Remove debug * Updating response time log to be set by bid response * Adding screen width/height to request * Test fix * Test fix * Removing interpretResponse signaling * Simplifying send data function Co-authored-by: Wei Wong Co-authored-by: Andy Rusiecki * Prebid 6.28.0 release * Increment version to 6.29.0-pre * CAPT-74: Pass ext section of each bid for prebid slot ID and GPID (#8509) Co-authored-by: Timothy M. Ace * Insticator Bid adapter: Adds Support for Video (#8452) * feat: added support for video * feat: added support for mimes * feat: added mimes check to video validation * Gdpr Enforcement module and sharedId/pubCommonId modules: vendor consent should not be enforced for first-party-id modules (#8448) * Fixed issue with gdprEnforcement module and sharedId/pubCommonId modules: vendor consent should not be enforced for first-party-id modules * addressed review comments * addressed review comments * added test to ensure device access is not allowed for vendorless modules in case purpose 1 consent isn't given * fixed issue with missing moduleType param Co-authored-by: Serhii Holdun * PubMatic Analytics Adapter : Added support for logging multiple bids (#8517) * Changed net revenue to True * Removed groupm as alias * Pasing the alternateBidder values to translator * initial commit * Added adId for differentiation of bids * Fixed UT * Added UT for GroupM * Finding winning bid according to adId * worked on LGTM comment Co-authored-by: Azhar * AdOcean bid adapter: support for SupplyChain object (#8518) * AdOcean adapter - support for supply chain object * AdOcean adapter - support for supply chain object - use older constructs * AdOcean adapter - small fixes * Taboola Bid Adapter: api support for https (#8520) * create taboola adapter * create taboola adapter md * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * update the md * update the Maintainer email * * update MD page * refactor code for better readability * small fix in UT * * add privacy to the request builder * add relevant Ut * small fixes in UT * * code refactoring + add more accurate way to get page url and referer * add relevant Ut * small fixes in md * * code refactoring + gte user id * add relevant Ut * small fixes * * code refactoring + gte user id * add relevant Ut * small fixes * * update end point url * update UT * Update banner End point structure * small fixes + update epi url * remove the destruction from the bidResponse property * (update the unit tests) remove the destruction from the bidResponse property * fix tests * fix tests - run stubs on each test * rerun because of another adapter flaky test * rerun because of another adapter flaky test * fix cors issue, switch between height, width position * update badv, bcat to be based in the ortb2 to support prebid 7 new protocols + update Ut * retry run circleci * retry run circleci * pull from upstream update md (placement + pub ) * update badv, bcat UT * rerun build * rerun build * support storageAllowed restriction on unit tests for prebid 7 * create taboola adapter * create taboola adapter md * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * update the md * update the Maintainer email * * update MD page * refactor code for better readability * small fix in UT * * add privacy to the request builder * add relevant Ut * small fixes in UT * * code refactoring + add more accurate way to get page url and referer * add relevant Ut * small fixes in md * * code refactoring + gte user id * add relevant Ut * small fixes * * code refactoring + gte user id * add relevant Ut * small fixes * * update end point url * update UT * Update banner End point structure * small fixes + update epi url * remove the destruction from the bidResponse property * (update the unit tests) remove the destruction from the bidResponse property * fix tests * fix tests - run stubs on each test * rerun because of another adapter flaky test * rerun because of another adapter flaky test * fix cors issue, switch between height, width position * update badv, bcat to be based in the ortb2 to support prebid 7 new protocols + update Ut * retry run circleci * retry run circleci * pull from upstream update md (placement + pub ) * update badv, bcat UT * rerun build * rerun build * support storageAllowed restriction on unit tests for prebid 7 * support storageAllowed restriction on unit tests for prebid 7 * add it also to the aftereach * add it also to the aftereach * change the api endpoint https protocol * ixBidAdapter: change to new endpoint and README (#8529) * Vidazoo bid adapter: fix spurious test failure (#8515) * Added support for IDs for multiple sources (#8499) Co-authored-by: Nick Curry * Prebid 6.29.0 release * Increment version to 7.0.0-pre * Prebid 7 (#8530) * Prebid 7: Delete userid targeting module (#8227) * Delete userIdTargeting.js * Delete userIdTargeting.md * Delete shareUserIds_spec.js * NextRoll ID: Remove NextRoll ID module (#8150) * Remove Sortable Analytics Adapter (#8251) * Prebid 7: Merge in changes from master (#8278) * only map one slotrender to one adunit (#8211) * DFP Ad Server Video: respect original url (#8168) * original url components take precedence over defaults uses object assignment * tests that url is respected * respects url size and cust params * moves url cust param addition to fn * tests that url params are respected * Admaru adapter : add new bid adapter (#8149) * init * modified admaruBidAdapter.js, md, _spec.js * modify for test * Delete .project * update * update admarubidadapter.js * Revert "Delete .project" This reverts commit 0e1bdd4fcadd0a97fea87ba2a92cb502e5e3a19b. * remove .project * modified * modified * Delete .project * modified * Revert "Delete .project" This reverts commit c4e7bd6096fe9521dd5e2fab2b3d5241149dc6ec. * Delete .project * modified * modified Co-authored-by: sung.chung * Missena Bid Adapter - allow custom endpoint. (#8222) * Next Millenium Bid Adapter: Added new parameter group_id (#8200) * changed name company * changed name company in test * Added processing of a new group_id parameter * Added processing of a new group_id parameter * changed check parameters * fixed lint remarks * added test * fixed bug - lint * changed test * changed test - 2 * fixed bug - adapter * add timeout value to timeout pixel (#8224) * Outbrain bid adapter: added floor module and privacy link support (#8223) * add floor support * add additional validation for bid request format * add privacy link support * fixes * set privacy in mapper * fix test * Improve Digital Bid adapter: use the oRTB server endpoint (#8138) * Major refactoring to use new oRTB server endpoint Co-authored-by: Faisal Islam <100519197+iosfaisal@users.noreply.github.com> Co-authored-by: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> * fixed wrong merge * Fixed issue related to site and app (#9) * Fixed errors for test cases. Co-authored-by: Faisal Islam <93644923+faisalvs@users.noreply.github.com> Co-authored-by: Faisal Islam <100519197+iosfaisal@users.noreply.github.com> Co-authored-by: Faisal Islam * NativoBidAdapter - Bid data mapping refactor and added QS params on request (#8196) * Initial nativoBidAdapter document creation (js, md and spec) * Fulling working prebid using nativoBidAdapter. Support for GDPR and CCPA in user syncs. * Added defult size settings based on the largest ad unit. Added response body validation. Added consent to request url qs params. * Changed bidder endpoint url * Changed double quotes to single quotes. * Reverted package-json.lock to remove modifications from PR * Added optional bidder param 'url' so the ad server can force- match an existing placement * Lint fix. Added space after if. * Added new QS param to send various adUnit data to adapter endpopint * Updated unit test for new QS param * Added qs param to keep track of ad unit refreshes * Updated bidMap key default value * Updated refresh increment logic * Refactored spread operator for IE11 support * Updated isBidRequestValid check * Refactored Object.enties to use Object.keys to fix CircleCI testing errors * Updated bid mapping key creation to prioritize ad unit code over placementId * Added filtering by ad, advertiser and campaign. * Merged master * Added more robust bidDataMap with multiple key access * Deduped filer values * Rolled back package.json * Duped upstream/master's package.lock file ... not sure how it got changed in the first place * Small refactor of filterData length check. Removed comparison with 0 since a length value of 0 is already falsy. Co-authored-by: Joshua Fledderjohn * Floor price : allow having a 0$ floor (#8239) * Prebid 6.18.0 release * Increment version to 6.19.0-pre * IAS RTD Module: Custom key values (#8214) * Mapping table initialization A constant initialises a mapping table where each possible key used by IAS is mapped to itself. The dataProviders config for the IAS RTD module accepts an optional new property: keyMappings. The init function reads this property and uses it to overwrite any key that is already in the pre-initialised mapping table with the new value provided. In a future addition, the mapping table will be used to populate the key-values actually sent to the adserver. * Rename Key Values After merging the key-values for each ad unit, a renaming function is called to ensure that any custom key chosen by the client is used. * Bugfix Call the callback() function in getBidRequestData. * Unit tests changed to include one renamed parameter. * Unit tests corrected and adapted to cover the current features. * Yieldlab Bid Adapter: Add Support for User Matching (#8148) * Build system: add option to generate source maps for production builds (#8220) * Gamoshi Bid Adapter: Handle gdpr applies correctly (#8245) * Nobid Prebid Adapter commit (#4050) * Nobid Prebid Adapter commit * Fixed global replace and unit tests * Fixed find function * Added nobidBidAdapter.md * Removed description and added "Bid Params" section. * Added test siteId 2 for testing. * Refactored the Adapter to remove most references to the nobid object. We still need the nobid object because we have a passback tag in DFP that makes reference to it. * Fix concurrent responses on the page * Cosmetic change to log an error in case of missing ad markup * Keep nobid.bidResponses cross adapters. * Added GDPR support in user sync and added test coverage. gulp test-coverage gulp view-coverage * Padding issues * Fix padding issues * Fix padding * update outstream prod url (#4104) * support pubcid and uids (#4143) * Fix misspelling and minor cleanup of schain docs (#4150) * Prebid 2.31.0 Release * Increment pre version * Rubicon: tuning logged messages (#4157) * Rubicon: tuning logged messages * Update rubiconBidAdapter.js * fixed indentation * Rubicon Video COPPA fix (#4155) * Rubicon Video COPPA fix * Unit test for Rubicon Video COPPA fix * Playground XYZ adapter - iframe usersync bug fix (#4141) * corrected user sync type * removed support for iframe usersync * added unit tests for getUserSyncs * update nvmrc file (#4162) * update gulp-footer package (#4160) * Datablocks bid/analytics adapter (#4128) * add datablocks Analytics and Bidder Adapters * remove preload param * remove preloadid * better coverage of tests * better coverage * IE doesn't support array.find * lint test * update example host * native asset id should be integer * update logic of ad_types field in appnexusBidAdapter (#4065) * Shorten SomoAudience to just Somo (#4163) * Shorten SomoAudience to just Somo * Make package-lock return * Quantcast: Fix for empty video parameters (#4145) * Copy params from bid.params.video. * Added test for missing video parameters. * Include mimes from adunit. * One Video adding Rewarded Video Feature (#4142) * outstream changes * removing global filtet * reverting page * message * adapter change * remove space * testcases * testpage * spaces for test page * renderer exist case * reverting package-lock.json * adding schain object * adding tagid * syntaxx error fix * video.html * space trailing * space * tagid * inventoryId and placement * rewarded video * added unit test case * Module to pass User Ids to DFP (#4140) * first commit * renamed * minor doc change * documentation * small change * EB * removed unused imports * minor changes * reanmaed a const * adding more methods to test shareUserIds module * unit tets cases for shareUserIds * indentation * renamed DFP to GAM * renamed shareUserIds to userIdTargeting * Update userIdTargeting.md * trying to restart CI * digitrust userId case handled * minor comment change * using auctionEnd event instead of requestBids.before * using events.on * Buzzoola bid adapter (#4127) * initial commit for buzzoola adapter * leave only banners for now * fix bid validation * change endpoint url * add video type * restore renderer * fix renderer * add fixed player sizes * switch bids * convert dimentions to strings * write tests * 100% tests * remove new DOM element creation in tests * handle empty response from server * change description * E2e tests for Native and Outstream video Ad formats. (#4116) * reorganize e2e/ tests into separate directories * new test page for e2e-banner testing * add test to check if Banner Ad is getting loaded * change location of the spec files to reflect change in test/e2e directory structure * add test case to check for generation of valid targeting keys * create Native Ad test page * add test case to check validity of the targeting keys and correct rendering of the Ad * update old browser versions to new * update browser version * update title * remove console.log statements * add basic functional test for e2e outstream video ad format * Update LockerDome adUnitId bid param (#4176) This is not a breaking change * fix several issues in appnexus video bids (#4154) * S2s testing disable client side (#4123) * Add microadBidAdapter * Remove unnecessary encodeURIComponent from microadBidAdapter * Submit Advangelists Prebid Adapter * Submit Advangelists Prebid Adapter 1.1 * Correct procudtion endpoint for prebid * analytics update with wrapper name * reverted error merge * New testServerOnly flag * Tests and a bug fix * Removed dead code * Fixes requested in review * Check each adUnit * isTestingServerOnly changes per Eric * Fixed IE 11 bug * More tests * improved test case names * New option to Include deal KVPs when enableSendAllBids === false (#4136) * new option to include KVPs which have deals when enableSendAllBids === false * updating tests to be more realistic * Prebid 2.32.0 Release * increment pre version * Rubicon doc: changing video test zone (#4187) * added schain support to sonobi adapter (#4173) * if schain config is not defined then error should not be thrown (#4165) * if schain config is not defiend then error should not be thrown * relaxed mode nodes param not defined error handled * added test cases for config validation * a curly bracket was missing in the example * Rubicon: updating test params (#4190) * myTargetBidAdapter: support currency config (#4188) * Update README.md (#4193) * Update README.md * Update README.md * cedato bid adapter instream video support (#4153) * Added adxpremium prebid analytics adapter (#4181) * feat(OAFLO-186): added support for schain (#4194) * Sonobi - send entire userid payload (#4196) * added userid param to pass the entire userId payload to sonobis bid request endpoint * removed console log git p * fixed lint * OpenX Adapter fix: updating outdated video examples (#4198) * userId - Add support for refreshing the cached user id (#4082) * [userId] Added support for refreshing the cached user id: refreshInSeconds storage parameter, related tests and implementation in id5 module * [userId] Added support for refreshing the cached user id: refreshInSeconds storage parameter, related tests and implementation in id5 module * UserId - ID5 - Updated doc with new contact point for partners * UserId - Merged getStoredValue and getStoredDate * [UserId] - ID5 - Moved back ID5 in ./modules * UserId - ID5 - Fixed incorrect GDPR condition * [UserId] - Doc update and test cleanup * Prebid 2.33.0 Release * Increment pre version * SupplyChainObject support and fires a pixel onTimeout (#4152) * - Implemented the 'onTimeout' callback to fire a pixel when there's a timeout. - Added the ability to serialize an schain object according to the description provided here: https://github.com/InteractiveAdvertisingBureau/openrtb/blob/master/supplychainobject.md * some mods to the schain tag generation * - added tests for schain param checking. * - fixed a malformed url for timeouts * - Removed a trailing ',' while generating a schain param. * - Using the schain object from validBidRequest if present. Reverting to checking if params has it if not. * - reverting changes to merge with master * - Resolving merge issues * Feature/add profile parameter (#4185) * Add optional profile parameter * EMXDigital Bid Adapter: Add video dimensions in request (#4174) * addressed feedback from #3731 ticket * removed commented code from emx test spec * logging removed from spec * flip h & w values from playerSize for video requests * adding Outstream mediaType to EMX Digital * adding device info. update to grab video param. styling changes. * add video dimensions from playerSize * fix test for video dimensions * Added keywords parameter support in TrustX Bid Adapter (#4183) * Add trustx adapter and tests for it * update integration example * Update trustx adapter * Post-review fixes of Trustx adapter * Code improvement for trustx adapter: changed default price type from gross to net * Update TrustX adapter to support the 1.0 version * Make requested changes for TrustX adapter * Updated markdown file for TrustX adapter * Fix TrustX adapter and spec file * Update TrustX adapter: r parameter was added to ad request as cache buster * Add support of gdpr to Trustx Bid Adapter * Add wtimeout to ad request params for TrustX Bid Adapter * TrustX Bid Adapter: remove last ampersand in the ad request * Update TrustX Bid Adapter to support identical uids in parameters * Update TrustX Bid Adapter to ignore bids that sizes do not match the size of the request * Update TrustX Bid Adapter to support instream and outstream video * Added wrapperType and wrapperVersion parameters in ad request for TrustX Bid Adapter * Update TrustX Bid Adapter to use refererInfo instead depricated function utils.getTopWindowUrl * HOTFIX for referrer encodind in TrustX Bid Adapter * Fix test for TrustX Bid Adapter * TrustX Bid Adapter: added keywords passing support * rubicon: avoid passing unknown position (#4207) * rubicon: not passing pos if not specified * added comment * not sending pos for video when undefined * cleaning up test * fixed unit test * correctly reference bidrequest and determine mediatype of bidresponse (#4204) * GumGum: only send gdprConsent when found (#4205) * adds digitrust module, mods gdpr from bool to int * update unit test * only send gdprconsent if present * LKQD: Use refererInfo.referer as fallback pageurl (#4210) * Refactored URL query parameter passthrough for additional values, changed SSP endpoint to v.lkqd.net, and updated associated unit tests * Use refererInfo.referer as fallback pageurl * Removed logs and testing values * [UserId] - ID5 - Fixed case when consentData is undefined (No CMP) (#4215) * create stubs for localStorage in widespaceBidAdapter test file (#4208) * added adId property to adRenderFailed event (#4097) When no bid (therefore no adUnitCode) is available in the adRenderFailed event it can be difficult to identify the erroring slot.But in almost all cases the given slot still has the adId targeting. * OpenX Adapter: Forcing https requests and adding UserID module support for LiveRamp and TTD (#4182) * OpenX Adapter: Updated requests to force https * OpenX Adapter: Added support for TTD's UnifiedID and LiveRamp's IDL * PubMatic to support userId sub-modules (#4191) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * TripleLift support for UnifiedId and IdentityLink (#4197) * Add IdentityLink support and fix UnifiedId. It appears we've been looking for UnifiedId userIds on the bidderRequest object, when they are found on bidRequests. This commit fixes that error, and adds support for IdentityLink. * change maintainer email to group * Added lemma adapter (#4126) * lemmaBidAdapter.js Added lemma bid adapter file * lemmaBidAdapter.md Added lemma bid adapter md file * lemmaBidAdapter_spec.js Added lemma bid adapter test spec file * Update lemmaBidAdapter.js Fixed automated code review alert comparison between inconvertible types * Update lemmaBidAdapter.js Fixed review changes * Update lemmaBidAdapter.md Correct parameter value. * Adkernel adapter new alias (#4221) * Force https scheme for Criteo Bidder (#4227) * assign adapter version number * Ensure that Criteo's bidder is always called through https * Add Video Support for Datablocks Bid Adapter (#4195) * add datablocks Analytics and Bidder Adapters * remove preload param * remove preloadid * better coverage of tests * better coverage * IE doesn't support array.find * lint test * update example host * native asset id should be integer * add datablocks Video * remove isInteger * skip if empty * update adUnit, bidRequest and bidResponse object (#4180) * update adUnit, bidRequest and bidResponse object * add test for mediaTypes object * 3 display banner and video vast support for rads (#4209) * add stv adapter * remove comments from adapter file * start rads adapter * fix adapter and tests * fixes * fix adapter and doc * fix adapter * fix tests * little fix * add ip param * fix dev url * #3 radsBidAdapter.md * #3 radsBidAdapter.md: cleanup * fix code and doc * UserId - Add SameSite and server-side pubcid support (#3869) * Add SameSite and server-side pubcid support * Fix emoteevBidAdapter unit test * added schain to appnexus bid adapter (#4229) * added schain to appnexus bid adapter * semicolon * update doubleclick url (#4179) * Prebid 2.34.0 release * increment pre version * Rubi Analytics handles > 1 bidResponse per bidRequest (#4224) * videoNow bid adapter (#4088) * -- first commit * -- cors and bidder's name fixed * -- almost ready * -- added docs * -- added nurl tracking * -- bid params * -- tests added * -- test fixed * -- replace placeholder in the onBidWon pixel's url * -- commit for restart tests * -- change response data format for display ad * -- tests updated * -- 100% tests coverage * -- a few clean the test's code * -- custom urls from localStorage * -- tests updated * -- a few clean the test's code * -- new init model * -- spec for new init model * -- fix for new init model * -- code cleaned * -- 100% tests coverage * -- 100% tests coverage * -- fixed test * -- commit for restart tests * djax new bidder adapter (#4192) * djax bidder adapter * djax bidder adapter * Update hello_world.html * Added Turk Telekom Bid Adapter (#4203) * Added Turk Telekom Bid Adapter * Fix md file for Turk Telekom Bid Adapter * MicroAd: Use HTTPS in all requests (#4220) * Always use HTTPS endpoint in MicroAd * Update code * Fixed a broken test in MicroAd * Schain: avoiding Object.values as it is breaking on IE11 (#4238) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * avoiding use of Object.values * 3952 delay auction for ids (#4115) * 3952 delay auction for user ids * 3952 add integration example * 3952 add tests * 3952 fix html example * add todos * 3952 continue auction if ids received * 3952 add tests for auction delay * increase test coverage * set config for test * remove todo * add a few more checks to tests * add comment, force tests to rerun * Feature: adUnitBidLimit (#3906) * added new feature to config to limit bids when sendallbids is enabled * cleaned up code. removed extra spaces etc * removed trailing spaces in config * remove .flat() and replaced with spread operator * removed flat function and instead pushing using spread operator * updated to use sendBidsControl instead * updated targeting_spec to test bidLimit * removed trailing spaces from targeting_spec * Update Rubicon Adapter netRevenue default (#4242) * Add microadBidAdapter * Remove unnecessary encodeURIComponent from microadBidAdapter * Submit Advangelists Prebid Adapter * Submit Advangelists Prebid Adapter 1.1 * Correct procudtion endpoint for prebid * analytics update with wrapper name * reverted error merge * update changed default value of netRevenue to true * Removed AdastaMadia from alias (#4255) * Update appnexusBidAdapter.js (#4251) * IdentityLink - change expiration time to 30 days (#4239) * Add coppa support for AppNexus adapter (#4253) * Add coppa support for AppNexus adapter * test name * add new longform e2e tests (#4206) * Konduit module (#4184) * Adding Konduit module * Removed superfluous arguments passed to obtainVastUrl function * Removed superfluous arguments passed to obtainVastUrl function. * Build trigger (empty commit) * Module documentation updated according to the comments * Logic in obtainVastUrl function updated according to the review comment. * Removed hook, enabled eslint * Circle CI runs e2e tests on every push (#4200) * run functional tests on circle ci on push to any remote branch * remove extraneous key from config file * add test.localhost as alias to 127.0.0.1 * check 0: execute circle-ci * move /etc/config to a separate command * change bid partner to rubicon * test appnexus bid adapter in ci * comment browserstack command * remove console.log statement * test1: circle-ci * change reference dev -> prod while loading prebid * add console.log statement * check-2: circle-ci * comment browserstack testing * change bid adapter * change bid adapter * remove test case for checking targeting keys * remove the ci flag * uncomment test for checking correct generation of targeting keys * swap AN -> Rubicon for testing targeting keys * Outcon bid adapter. (#4161) * Outcon bid adapter. * Fix identation * Fixes * Fixes * Fixes * Spec fixes * Fixes * Fix urls * Fix * Fix parameters * Fix space operators * Fix bidder timeout * Update * Fix whitespace * no message * Outcon unit test * no message * no message * no message * no message * Fixes * Fixes * Change url * no message * no message * no message * Added bidId * no message * no message * no message * no message * Wrapping url with html * no message * no message * no message * Adding workflow to run end to end tests (#4230) * Adding workflow to run end to end tests * trying self branch * Update to run at 12 every day * cleanup config using aliases * update branch and cron time * add command * update prebid path for e2e test pages (#4274) * Prebid 2.35.0 release * Increment pre version * Add usersync to adpone adapter (#4245) * add user sync to adpone adapter * move adpone usersync to global variable * added withcredentials to http request * fix http request options * fix http request options * add withCredentials: true * add withCredentials: true * added test coverage to usersync * update sync function * add test coverage * adpone adapter * package lock * add more testing * add more testing * testing for onBidWon fucntion * test onbidwon function * trigger build * Revert GumGum Adapter 2.28 resizing changes (#4277) * changed resizing unit tests to return the first size dimensions in the sizes array * added some changes * reverted adapter changes * SpotX Bid Adapter: Support schain, ID5 object, Google consent object, and hide_skin (#4281) * Add SpotXBidAdapter * Minor updates * Undo testing changes to shared files * Fix relative imports * Remove superfluous imports and write a few more tests * Formatting, ID5 object, Google consent objects - Added ID5 object support - Added Google Consent object - Reformatted indentaiton on spec file * Revert content_width and content_height changes in docs - not sure how these got moved, lets put them back * Remove click_to_replay flag in example - no reason to use this one in the example * Spotx adapter - Add schain support and update unit tests * Update schain path in ORTB 2.3 request body - schain object is now added to ortb request body at request.ext.source.ext.schain * Add hide_skin to documentation - whoops, this got removed, let's add it back * Update Rubicon Analytics Adapter `bidId` to match PBS (#4156) * Add microadBidAdapter * Remove unnecessary encodeURIComponent from microadBidAdapter * Submit Advangelists Prebid Adapter * Submit Advangelists Prebid Adapter 1.1 * Correct procudtion endpoint for prebid * analytics update with wrapper name * reverted error merge * update for rubicon analytics to send seat[].bid.id for PBS video and banner * fixed conditional for server and video or banner * updated with optimized value test for bidid * update changed default value of netRevenue to true * remove var declaration for rightSlot to correct lgtm error for unused variable * update defineSlot div id to match div id defined in html body * update test ad unit test props * revert lock to match remote master * add seatBidId to bidObj in rpBidAdapter interpretResponse * update setTargeting to execute in the bids back handler * remove dev integration test page * meaningless commit to get lgtm to re-run * SmartRTB adapter update (#4246) * modules: Implement SmartRTB adapter and spec. * Fix for-loop syntax to support IE; refactor getDomain out of exported set. * Remove debugs, update doc * Update test for video support * Handle missing syncs. Add video to media types in sample ad unit * Add null response check, update primary endpoint * Note smrtb video requires renderer * Support Vast Track (#4276) * Add microadBidAdapter * Remove unnecessary encodeURIComponent from microadBidAdapter * Submit Advangelists Prebid Adapter * Submit Advangelists Prebid Adapter 1.1 * Correct procudtion endpoint for prebid * analytics update with wrapper name * reverted error merge * update changed default value of netRevenue to true * Add parameters if config.cache.vasttrack is true * Use requestId instead of adId * Test new vasttrack payload params * Removed commented out code * Relaxed conditional check per review * Removed commented out line * Added 1000x250 size (#4295) * prepare vidazoo adapter for v3.0 (#4291) * Improve Digital adapter: support schain (#4286) * LiveIntent Identity Module. (#4178) * LiveIntentIdSystem. Initial implementation. * LiveIntentIdSystem. Removed whitespace. * Fixed typo * Renamed variables, cookiesm added md. * Changed the default identity url. * Composite id, with having more than just the lipbid passed around. * Composite id. * Merge conflict resolution. * Changed docs and param description. * Added typedoc & mentioned liveIntentIdSystem in submodule.json. * Extracted the LiveIntentIdSystem under modules, removed it from default userId modules. * Fixing the 204 + no body scenario. * Added liveIntent to submodule.json * Fixing docs indentation. * Updated prebidServer & specs. * Minor specs update. * updating liveintent eids source (#4300) * updating liveintent eids source these are supposed to be domains * updating unit test * fix appnexusBidAdapter view-script regex (#4289) * fix an view script regex * minor syntax update * 33Across adding bidder specific extension field (#4298) * - add 33across specific ext field for statedAt * - fix unit test for 33Across adapter * PubMatic to support LiveIntent User Id sub-module (#4306) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * supporting LiveIntent Id in PubMatic adapter * updated source for liveintent * Finteza Analytics Adapter: fix cookies (#4292) * fix reading and sending cookies * fix lint errors * clear comments * add unit tests * fix calling of setCookies for IE * clear cookies after test * use own setCookie method inside tests * Update LockerDome adapter to support Prebid 3.0 (#4301) * Returning the `IdResponse` type with an obj + callback. Fix for 4304 (#4305) * Returning the `IdResponse` type with an obj + callback. * Renamed resp -> result. * Removed whitespace. * ShowHeroes adapter - expanded outstream support (#4222) * add ShowHeroes Adapter * ShowHeroes adapter - expanded outstream support * Revert "ShowHeroes adapter - expanded outstream support" This reverts commit bfcdb913b52012b5afbf95a84956b906518a4b51. * ShowHeroes adapter - expanded outstream support * ShowHeroes adapter - fixes (#4222) * ShowHeroes adapter - banner and outstream fixes (#4222) * ShowHeroes adapter - description and outstream changes (#4222) * ShowHeroes adapter - increase test coverage and small fix * [Orbidder-Adapter] Add bidRequestCount and remove bid.params.keyValues (#4264) * initial orbidder version in personal github repo… * update permutiveRtdProvider.md * update permutiveRtdProvider.md * ID -> 'bidder code' * add GDPR clause + changed SDK permission bit * supported bidders -> bidders with known support Co-authored-by: Guy Dawson Co-authored-by: Sam Desborough Co-authored-by: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Co-authored-by: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Co-authored-by: TPMN Admin Co-authored-by: changjun Co-authored-by: jxdeveloper1 <71084096+jxdeveloper1@users.noreply.github.com> Co-authored-by: David Carver <54326287+david-carver@users.noreply.github.com> Co-authored-by: David Spohr Co-authored-by: Demetrio Girardi Co-authored-by: Mehdi Bouallagui <45876988+mbouallagui@users.noreply.github.com> Co-authored-by: Robert Ray Martinez III Co-authored-by: Dennis Co-authored-by: Alexander <32703851+pro-nsk@users.noreply.github.com> Co-authored-by: Alexander Bogdanov Co-authored-by: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Co-authored-by: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Co-authored-by: Azhar Co-authored-by: JulianRSL <88969792+JulianRSL@users.noreply.github.com> Co-authored-by: Julian Co-authored-by: Patrick McCann Co-authored-by: Remi Demol Co-authored-by: Olivier Co-authored-by: Léonard Labat Co-authored-by: Karim Mourra Co-authored-by: joseluis laso Co-authored-by: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Co-authored-by: Wojciech Biały Co-authored-by: Prebid.js automated release Co-authored-by: John Wright Co-authored-by: mihaisandu07 <102022853+mihaisandu07@users.noreply.github.com> Co-authored-by: Tiago Peczenyj Co-authored-by: jkthomas Co-authored-by: Tomasz Januszek Co-authored-by: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Co-authored-by: Gena Co-authored-by: Love Sharma Co-authored-by: Love Sharma Co-authored-by: kalidas-alkimi <92875788+kalidas-alkimi@users.noreply.github.com> Co-authored-by: Alexander Bogdanov Co-authored-by: motors Co-authored-by: Cyprien Pannier Co-authored-by: shahinrahbariasl <56240400+shahinrahbariasl@users.noreply.github.com> Co-authored-by: shahin.rahbariasl Co-authored-by: Viktor Dreiling <34981284+3link@users.noreply.github.com> Co-authored-by: Viktor Dreiling Co-authored-by: Viktor Dreiling Co-authored-by: Hashiomoto <50349637+HashimotoLogly@users.noreply.github.com> Co-authored-by: Andy Rusiecki Co-authored-by: Faisal Islam <93644923+faisalvs@users.noreply.github.com> Co-authored-by: iosfaisal <100519197+iosfaisal@users.noreply.github.com> Co-authored-by: Federico Izuel Co-authored-by: Jonathan Nadarajah <50102657+jogury@users.noreply.github.com> Co-authored-by: Sacha <35510349+thebraveio@users.noreply.github.com> Co-authored-by: rahulgravito <105201950+rahulgravito@users.noreply.github.com> Co-authored-by: haruka-yamashita2 <39541428+haruka-yamashita2@users.noreply.github.com> Co-authored-by: Vikas Srivastava <30315503+visrivastava@users.noreply.github.com> Co-authored-by: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Co-authored-by: Surovenko Alexey Co-authored-by: Jason Lydon <95770514+ftxmoJason@users.noreply.github.com> Co-authored-by: Jason Lydon Co-authored-by: johnwier <49074029+johnwier@users.noreply.github.com> Co-authored-by: rajsidhunovatiq <79534312+rajsidhunovatiq@users.noreply.github.com> Co-authored-by: novatiq <79258366+novatiq@users.noreply.github.com> Co-authored-by: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Co-authored-by: Alexander Clouter Co-authored-by: Chris Pabst Co-authored-by: Maxim Pakhomov Co-authored-by: Maxim Pakhomov <70204615+maximpakhomov@users.noreply.github.com> Co-authored-by: Bendegúz Ács <30595431+acsbendi@users.noreply.github.com> Co-authored-by: José Venâncio <45434454+jvnnc@users.noreply.github.com> Co-authored-by: Jose Co-authored-by: mikiz <31058500+mikizi@users.noreply.github.com> Co-authored-by: Prebid-Team Co-authored-by: Mohit Patil Co-authored-by: Monis Qadri Co-authored-by: monis.q Co-authored-by: guiann Co-authored-by: Catalin Ciocov Co-authored-by: Chris Huie Co-authored-by: Paris Holley Co-authored-by: shubham.si <60253122+shubham-si@users.noreply.github.com> Co-authored-by: Kanchika - Automatad Co-authored-by: bretg Co-authored-by: Jurij Sinickij Co-authored-by: Jeremy Sadwith Co-authored-by: Wei Wong Co-authored-by: Timothy Ace Co-authored-by: Timothy M. Ace Co-authored-by: Eugene Vigonny <79149590+EugeneVigonny@users.noreply.github.com> Co-authored-by: Serhii Holdun Co-authored-by: Serhii Holdun Co-authored-by: Marcin Muras <47107445+mmuras@users.noreply.github.com> Co-authored-by: Peiyuan <106841361+ix-peiyuan@users.noreply.github.com> Co-authored-by: Nick-Merkle <105746498+Nick-Merkle@users.noreply.github.com> Co-authored-by: Nick Curry Co-authored-by: Abimael Martinez Co-authored-by: Nic G Co-authored-by: supadm <98890970+supadm@users.noreply.github.com> Co-authored-by: sung.chung Co-authored-by: Petre Damoc Co-authored-by: Malkov Mikhail Co-authored-by: Yohan Boutin Co-authored-by: Mark Kuhar Co-authored-by: Faisal Islam Co-authored-by: jsfledd Co-authored-by: Joshua Fledderjohn Co-authored-by: JulieLorin Co-authored-by: Rocco Barbini <46724608+rbarbini-ias@users.noreply.github.com> Co-authored-by: nkloeber <100145701+nkloeber@users.noreply.github.com> Co-authored-by: Salomon Rada Co-authored-by: robdubois <53589945+robdubois@users.noreply.github.com> Co-authored-by: sumit116 Co-authored-by: nwlosinski Co-authored-by: Mike Chowla Co-authored-by: Bret Gorsline Co-authored-by: Artem Seryak Co-authored-by: Jonathan Mullins Co-authored-by: htang555 Co-authored-by: Bryan DeLong Co-authored-by: dpapworth-qc <50959025+dpapworth-qc@users.noreply.github.com> Co-authored-by: DeepthiNeeladri Co-authored-by: Harshad Mane Co-authored-by: Roman Co-authored-by: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Co-authored-by: Margaret Liu Co-authored-by: TJ Eastmond Co-authored-by: Jason Snellbaker Co-authored-by: JonGoSonobi Co-authored-by: Vladimir Fedoseev Co-authored-by: DJ Rosenbaum Co-authored-by: Alex Khmelnitsky Co-authored-by: adxpremium <55161519+adxpremium@users.noreply.github.com> Co-authored-by: Jimmy Tu Co-authored-by: Pierre-Antoine Durgeat Co-authored-by: Eric Harper Co-authored-by: Telaria Engineering <36203956+telariaEng@users.noreply.github.com> Co-authored-by: ujuettner Co-authored-by: Dan Bogdan <43830380+EMXDigital@users.noreply.github.com> Co-authored-by: PWyrembak Co-authored-by: susyt Co-authored-by: Max Crawford Co-authored-by: Pascal S Co-authored-by: Will Chapin Co-authored-by: Lemma Dev <54662130+lemmadev@users.noreply.github.com> Co-authored-by: Denis Logachov Co-authored-by: onlsol <48312668+onlsol@users.noreply.github.com> Co-authored-by: Paul Yang Co-authored-by: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Co-authored-by: Mike Sperone Co-authored-by: sdbaron Co-authored-by: djaxbidder <55269794+djaxbidder@users.noreply.github.com> Co-authored-by: turktelssp <54801433+turktelssp@users.noreply.github.com> Co-authored-by: nkmt <45026101+strong-zero@users.noreply.github.com> Co-authored-by: Mutasem Aldmour Co-authored-by: r-schweitzer <50628828+r-schweitzer@users.noreply.github.com> Co-authored-by: Isaac A. Dettman Co-authored-by: Adasta Media <55529969+Adasta2019@users.noreply.github.com> Co-authored-by: mamatic <52153441+mamatic@users.noreply.github.com> Co-authored-by: Konduit <55142865+konduit-dev@users.noreply.github.com> Co-authored-by: TinchoF <50110327+TinchoF@users.noreply.github.com> Co-authored-by: Jaimin Panchal <7393273+jaiminpanchal27@users.noreply.github.com> Co-authored-by: Jaimin Panchal Co-authored-by: Sergio Co-authored-by: Wayne Yang Co-authored-by: Cody Bonney Co-authored-by: evanmsmrtb Co-authored-by: hdeodhar <35999856+hdeodhar@users.noreply.github.com> Co-authored-by: Oz Weiss Co-authored-by: Janko Ulaga Co-authored-by: thomas-33across <44033452+thomas-33across@users.noreply.github.com> Co-authored-by: Finteza Analytics <45741245+finteza@users.noreply.github.com> Co-authored-by: Vadim Mazzherin Co-authored-by: Hendrik Iseke <39734979+hiseke@users.noreply.github.com> Co-authored-by: Anand Venkatraman Co-authored-by: Eyas Ranjous Co-authored-by: Michael Co-authored-by: hbanalytics <55453525+hbanalytics@users.noreply.github.com> Co-authored-by: Index Exchange 3 Prebid Team Co-authored-by: Michael Kuryshev Co-authored-by: Roffray Co-authored-by: rumesh Co-authored-by: oasis <2394426+bmwcmw@users.noreply.github.com> Co-authored-by: Nepomuk Seiler Co-authored-by: John Salis Co-authored-by: OneTagDevOps <38786435+OneTagDevOps@users.noreply.github.com> Co-authored-by: Ankit Prakash Co-authored-by: Dan Co-authored-by: romanantropov <45817046+romanantropov@users.noreply.github.com> Co-authored-by: msm0504 <51493331+msm0504@users.noreply.github.com> Co-authored-by: vrtcal-dev <50931150+vrtcal-dev@users.noreply.github.com> Co-authored-by: colbertk <50499465+colbertk@users.noreply.github.com> Co-authored-by: Steve Alliance Co-authored-by: bjorn-lw <32431346+bjorn-lw@users.noreply.github.com> Co-authored-by: 7XBID00 <52267720+7XBID00@users.noreply.github.com> Co-authored-by: Tomas Kovtun Co-authored-by: Jonathan Mullins Co-authored-by: ucfunnel <39581136+ucfunnel@users.noreply.github.com> Co-authored-by: ACannuniRP <57228257+ACannuniRP@users.noreply.github.com> Co-authored-by: Hugo Duthil Co-authored-by: skazedo Co-authored-by: 胡雨軒 Петр Co-authored-by: Konrad Dulemba Co-authored-by: Mariya Mego <31904600+mash-a@users.noreply.github.com> Co-authored-by: Daniel Cassidy Co-authored-by: kpis-msa <50609476+kpis-msa@users.noreply.github.com> Co-authored-by: omerBrowsi <54346241+omerBrowsi@users.noreply.github.com> Co-authored-by: Marcian123 Co-authored-by: koji-eguchi <50477903+koji-eguchi@users.noreply.github.com> Co-authored-by: sourabhg Co-authored-by: Alexis Andrieu Co-authored-by: Vlad Gurgov Co-authored-by: Richard Lee <14349+dlackty@users.noreply.github.com> Co-authored-by: Alex Co-authored-by: Vladislav Yatsun Co-authored-by: vincentproxistore <56686565+vincentproxistore@users.noreply.github.com> Co-authored-by: Rich Snapp Co-authored-by: Rade Popovic <32302052+nanointeractive@users.noreply.github.com> Co-authored-by: Matt Quirion Co-authored-by: rhythmonebhaines <49991465+rhythmonebhaines@users.noreply.github.com> Co-authored-by: binoy-chitale Co-authored-by: Alex Pashkov Co-authored-by: harpere Co-authored-by: Scott Co-authored-by: tadam75 Co-authored-by: Veronica Kim <43146383+vkimcm@users.noreply.github.com> Co-authored-by: songtungmtp <57524426+songtungmtp@users.noreply.github.com> Co-authored-by: z-sunshine <33084773+z-sunshine@users.noreply.github.com> Co-authored-by: Sander Co-authored-by: Moshe Moses Co-authored-by: Ignat Khaylov Co-authored-by: Ignat Khaylov Co-authored-by: Bill Newman Co-authored-by: Vladislav Isaiko Co-authored-by: Aiholkin Co-authored-by: Mykhailo Yaremchuk Co-authored-by: Saveliev Taras Co-authored-by: Taras Saveliev Co-authored-by: Viktor Dreiling Co-authored-by: SmartyAdman <59048845+SmartyAdman@users.noreply.github.com> Co-authored-by: minoru katogi Co-authored-by: minoru katogi Co-authored-by: ADman Media Co-authored-by: SmartyAdman Co-authored-by: EddieYu <32135564+eddieyu1998@users.noreply.github.com> Co-authored-by: SeedingAllianceTech <55976067+SeedingAllianceTech@users.noreply.github.com> Co-authored-by: Jonas Hilsen Co-authored-by: llays Co-authored-by: Andrew Lays Co-authored-by: Jason Piros Co-authored-by: duancg Co-authored-by: Gabriel Chicoye Co-authored-by: Marcin Grzebyk <35067477+marcin15g@users.noreply.github.com> Co-authored-by: nllerandi3lift <75995508+nllerandi3lift@users.noreply.github.com> Co-authored-by: Dan Goldin Co-authored-by: Oleg Romanenko Co-authored-by: Grzegorz Sroka Co-authored-by: olafbuitelaar Co-authored-by: matt-vib <43612013+matt-vib@users.noreply.github.com> Co-authored-by: Mikhail Ivanchenko Co-authored-by: puhiza-d <103563100+puhiza-d@users.noreply.github.com> Co-authored-by: ramyferjaniadot <90328697+ramyferjaniadot@users.noreply.github.com> Co-authored-by: Audiencerun <57719351+audiencerun@users.noreply.github.com> Co-authored-by: anastasya123 <89073753+anastasya123@users.noreply.github.com> Co-authored-by: kapil-tuptewar <91458408+kapil-tuptewar@users.noreply.github.com> Co-authored-by: Kapil Tuptewar Co-authored-by: Benoit Liabastre Co-authored-by: thomasferal Co-authored-by: Thuy Hoang <61451682+thuyhq@users.noreply.github.com> Co-authored-by: Regulyarniy Nikolay Co-authored-by: Aleksandr Štšepelin Co-authored-by: e-volution-tech <61746103+e-volution-tech@users.noreply.github.com> Co-authored-by: mobfxoHB <74364234+mobfxoHB@users.noreply.github.com> Co-authored-by: Teddy Pierre <33702256+pierreted90@users.noreply.github.com> Co-authored-by: liliana-sortable Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: dsravana Co-authored-by: Patrick Loughrey Co-authored-by: Patrick Loughrey Co-authored-by: gbalboa0 Co-authored-by: gbalboa0 <47567497+gbalboa0@users.noreply.github.com> Co-authored-by: Michael Ermer Co-authored-by: Espen <2290914+espen-j@users.noreply.github.com> Co-authored-by: Vladyslav Laktionov Co-authored-by: vlad Co-authored-by: DmitriyMishutin <105477277+DmitriyMishutin@users.noreply.github.com> Co-authored-by: Dmitriy Mishutin Co-authored-by: Florent DANCY Co-authored-by: Philip Watson Co-authored-by: Tachfine Co-authored-by: Soni-Goyal Co-authored-by: Soni Goyal Co-authored-by: ffqs <47789178+ffqs@users.noreply.github.com> Co-authored-by: lasloche <62240785+lasloche@users.noreply.github.com> Co-authored-by: mihanikw2g <92710748+mihanikw2g@users.noreply.github.com> Co-authored-by: Nikulin Mikhail Co-authored-by: adquery <89853721+adquery@users.noreply.github.com> Co-authored-by: m.czerwiak Co-authored-by: annavane <101708287+annavane@users.noreply.github.com> Co-authored-by: AzureAD\AnnaVane Co-authored-by: micheletnc Co-authored-by: Naveen <172697+naveensrinivasan@users.noreply.github.com> Co-authored-by: Noam Tzuberi Co-authored-by: Noam Tzuberi Co-authored-by: Laslo Chechur Co-authored-by: OronW <41260031+OronW@users.noreply.github.com> Co-authored-by: Hendrick Musche <107099114+sag-henmus@users.noreply.github.com> Co-authored-by: TWrightSovrn <107207106+TWrightSovrn@users.noreply.github.com> Co-authored-by: victorlassomarketing <103455651+victorlassomarketing@users.noreply.github.com> Co-authored-by: anthonyjl92 Co-authored-by: Anthony Lin Co-authored-by: John Ivan Bauzon Co-authored-by: Lisa Benmore Co-authored-by: John Bauzon Co-authored-by: kyoya-takei Co-authored-by: yuu.t Co-authored-by: Yu Tong Co-authored-by: ym-ainiguez <89419575+ym-ainiguez@users.noreply.github.com> Co-authored-by: DianomiJH <84312830+DianomiJH@users.noreply.github.com> Co-authored-by: Michael Stevens Co-authored-by: Chris Almeida-Symons Co-authored-by: Martin Hill Co-authored-by: readpeaktuomo <66239046+readpeaktuomo@users.noreply.github.com> Co-authored-by: Serhii Kozlov Co-authored-by: StalmIlya <102592784+StalmIlya@users.noreply.github.com> Co-authored-by: Ilya Stalmahov Co-authored-by: Brian Schmidt Co-authored-by: jessoventes <82361050+jessoventes@users.noreply.github.com> Co-authored-by: Andy Rusiecki Co-authored-by: Ubuntu Co-authored-by: relaido <63339139+relaido@users.noreply.github.com> Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun Co-authored-by: n.maeura Co-authored-by: ccorbo Co-authored-by: Chris Corbo Co-authored-by: jennylt <48404417+jennylt@users.noreply.github.com> Co-authored-by: Michael Co-authored-by: Mathieu Pheulpin Co-authored-by: ako-adot <90328748+ako-adot@users.noreply.github.com> Co-authored-by: eknis Co-authored-by: Keisuke Kakinuma Co-authored-by: Prebid-bydata <71428180+Prebid-bydata@users.noreply.github.com> Co-authored-by: Jitendra-quizlet Co-authored-by: TM Co-authored-by: Tomasz Mielcarz Co-authored-by: Mikael Lundin Co-authored-by: fndigrazia Co-authored-by: Phaneendra Hegde Co-authored-by: Phaneendra Hegde Co-authored-by: newspassid-prebid <107485317+newspassid-prebid@users.noreply.github.com> Co-authored-by: JacobKlein26 <42449375+JacobKlein26@users.noreply.github.com> Co-authored-by: wsusrasp <106743463+wsusrasp@users.noreply.github.com> Co-authored-by: skoklowski Co-authored-by: Anass Seddiki Co-authored-by: Thomas Co-authored-by: sag-jonhil <78849369+sag-jonhil@users.noreply.github.com> Co-authored-by: Hendrick Musche Co-authored-by: rimaburder-index <55195208+rimaburder-index@users.noreply.github.com> Co-authored-by: samous Co-authored-by: Niass Co-authored-by: Samous Co-authored-by: Dejan Grbavcic Co-authored-by: Anass Seddiki Co-authored-by: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Co-authored-by: federico Co-authored-by: vfourny <109095331+vfourny-ogury@users.noreply.github.com> Co-authored-by: Michele Nasti Co-authored-by: Michele Nasti Co-authored-by: yieldlift <61510052+yieldlift@users.noreply.github.com> Co-authored-by: Danijel Predarski Co-authored-by: Kenan Gillet <1706856+kenan-gillet@users.noreply.github.com> Co-authored-by: Jared Co-authored-by: Skylinar <53079123+Skylinar@users.noreply.github.com> Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini Co-authored-by: Sławomir Kokłowski <38455696+skoklowski@users.noreply.github.com> --- modules/permutiveRtdProvider.md | 125 ++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 23 deletions(-) diff --git a/modules/permutiveRtdProvider.md b/modules/permutiveRtdProvider.md index 5fa6e14a474..f99389f82cc 100644 --- a/modules/permutiveRtdProvider.md +++ b/modules/permutiveRtdProvider.md @@ -1,9 +1,10 @@ -# Permutive Real-time Data Submodule +## Prebid Config for Permutive RTD Module -This submodule reads cohorts from Permutive and attaches them as targeting keys to bid requests. Using this module will deliver best targeting results, leveraging Permutive's real-time segmentation and modelling capabilities. +This module reads cohorts from Permutive and attaches them as targeting keys to bid requests. -## Usage +### _Permutive Real-time Data Submodule_ +#### Usage Compile the Permutive RTD module into your Prebid build: ``` @@ -31,26 +32,13 @@ pbjs.setConfig({ }) ``` -## Supported Bidders +#### Parameters -The Permutive RTD module sets Audience Connector cohorts as bidder-specific `ortb2.user.data` first-party data, following the Prebid `ortb2` convention, for any bidder included in `acBidders`. The module also supports bidder-specific data locations per ad unit (custom parameters) for the below bidders: - -| Bidder | ID | Custom Cohorts | Audience Connector | -| ------- | ---------- | -------------- | ------------------ | -| Xandr | `appnexus` | Yes | Yes | -| Magnite | `rubicon` | Yes | No | -| Ozone | `ozone` | No | Yes | - -Key-values details for custom parameters: - -- **Custom Cohorts:** When enabling the respective Activation for a cohort in Permutive, this module will automatically attach that cohort ID to the bid request. There is no need to enable individual bidders in the module configuration, it will automatically reflect which SSP integrations you have enabled in your Permutive dashboard. Permutive cohorts will be sent in the `permutive` key-value. - -- **Audience Connector:** You'll need to define which bidders should receive Audience Connector cohorts. You need to include the `ID` of any bidder in the `acBidders` array. Audience Connector cohorts will be sent in the `p_standard` key-value. - -## Parameters +The parameters below provide configurability for general behaviours of the RTD submodule, +as well as enabling settings for specific use cases mentioned above (e.g. acbidders). | Name | Type | Description | Default | -| ---------------------- | -------- | --------------------------------------------------------------------------------------------- | ------- | +|------------------------|----------|-----------------------------------------------------------------------------------------------|---------| | name | String | This should always be `permutive` | - | | waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (optional) | `false` | | params | Object | | - | @@ -58,12 +46,103 @@ Key-values details for custom parameters: | params.maxSegs | Integer | Maximum number of cohorts to be included in either the `permutive` or `p_standard` key-value. | `500` | | params.transformations | Object[] | An array of configurations for ORTB2 user data transformations | | -### The `transformations` parameter +##### The `transformations` parameter This array contains configurations for transformations we'll apply to the Permutive object in the ORTB2 `user.data` array. The results of these transformations will be appended to the `user.data` array that's attached to ORTB2 bid requests. -#### Supported transformations +##### Supported transformations | Name | ID | Config structure | Description | -| -------------- | --- | ------------------------------------------------- | ------------------------------------------------------------------------------------ | +|----------------|-----|---------------------------------------------------|--------------------------------------------------------------------------------------| | IAB taxonomies | iab | { segtax: number, iabIds: Object} | Transform segment IDs from Permutive to IAB (note: alpha version, subject to change) | + +#### Context + +Permutive is not listed as a TCF vendor as all data collection is on behalf of the publisher and based on consent the publisher has received from the user. +Rather than through the TCF framework, this consent is provided to Permutive when the user gives the relevant permissions on the publisher website which allow the Permutive SDK to run. +This means that if GDPR enforcement is configured _and_ the user consent isn’t given for Permutive to fire, no cohorts will populate. +As Prebid utilizes TCF vendor consent, for the Permutive RTD module to load, Permutive needs to be labeled within the Vendor Exceptions + +#### Instructions + +1. Publisher enables GDPR rules within Prebid. +2. Label Permutive as an exception, as shown below. +```javascript +[ + { + purpose: 'storage', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: ["permutive"] + }, + { + purpose: 'basicAds', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] + } +] +``` + +Before making any updates to this configuration, please ensure that this approach aligns with internal policies and current regulations regarding consent. + +## Cohort Activation with Permutive RTD Module + +### _Enabling Standard Cohorts_ + +**Note**: Publishers must be enabled on the above Permutive RTD Submodule to enable Standard Cohorts. + +The acbidders config in the Permutive RTD module allows publishers to determine which demand partners (SSPs) will receive standard cohorts via the user.data ortb2 object. Cohorts will be sent in the `p_standard` key-value. + +The Permutive RTD module sets standard cohort IDs as bidder-specific ortb2.user.data first-party data, following the Prebid ortb2 convention. + +There are **two** ways to assign which demand partner bidders (e.g. SSPs) will receive Standard Cohort information via the Audience Connector (acbidders) config: + +#### Option 1 - Automated + +New demand partner bidders may be added to the acbidders config directly within the Permutive Platform. + +**Permutive can do this on your behalf**. Simply contact your Permutive CSM with strategicpartnershipops@permutive.com on cc, +indicating which bidders you would like added. + +Or, a publisher may do this themselves within the UI using the below instructions. + +##### Create Integration + +In order to update acbidders via the Permutive dashboard, +it is necessary to first enable the prebid integration in the integrations page (settings). + +**Note on Revenue Insights:** The prebid integration includes a feature for revenue insights, +which is not required for the purpose of updating acbidders config. +Please see [this document](https://support.permutive.com/hc/en-us/articles/360019044079-Revenue-Insights) for more information about revenue insights. + +##### Update acbidders + +The input for the “Data Provider config” is currently a multi-input free text. +A valid “bidder code” needs to be entered in order to enable Standard Cohorts to be passed to the desired partner. +The [prebid Bidders page](https://docs.prebid.org/dev-docs/bidders.html) contains instructions and a link to a list of possible bidder codes. + +Acbidders can be added or removed from the list using this feature, however, this will not impact any acbidders that have been applied using the manual method below. + +#### Option 2 - Manual + +As a secondary option, new demand partner bidders may be added manually. + +To do so, a Publisher may define which bidders should receive Standard Cohorts by +including the _bidder code_ of any bidder in the `acBidders` array. + +**Note:** If a Publisher ever needs to remove a manually-added bidder, the bidder will also need to be removed manually. + +### _Enabling Custom Cohort IDs for Targeting_ + +Separately from Standard Cohorts - The Permutive RTD module also supports passing any of the **custom** cohorts created in the dashboard to some SSP partners for targeting +e.g. setting up publisher deals. For these activations, cohort IDs are set in bidder-specific locations per ad unit (custom parameters). + +Currently, bidders with known support for custom cohort targeting are: + +- Xandr +- Magnite + +When enabling the respective Activation for a cohort in Permutive, this module will automatically attach that cohort ID to the bid request. +There is no need to enable individual bidders in the module configuration, it will automatically reflect which SSP integrations you have enabled in your Permutive dashboard. +Permutive cohorts will be sent in the permutive key-value. From d06bdad3d6644b285e6aed1a6505fe6aec37d6c8 Mon Sep 17 00:00:00 2001 From: Michele Nasti Date: Tue, 26 Jul 2022 15:15:53 +0200 Subject: [PATCH 075/569] Prebid Core: switch native assets to ortb2 format (#8086) * switch native assets to ortb2 format * put rendererUrl, adTemplate back in message * rename ortb2 to ortb * typo fix * changes to support ortb for native media type * handle bid won case * mark bid as won on any post message for native * fix tests for bid_won event for native * add missing imports * native converting functions * convert new native ortb media type object to proprietary format for native bidders * fix LGTM.com issue * added comments; minor fixes * added test for convertOrtbRequestToProprietaryNative + fixes * add nativeParams to conversion * support nativeParams * check that when native.ortb is present, it's the only property * remove commented code * removed unnecessary tests * added test that checks that BID_WON is not fired more than once for the same adId * validation is now performed on ortb data only * fix for prebidServer_native_example.html * PrebidServerBidAdapter also responds in ortb format * fix aspect_ratios as an array in tests * LGTM fix - remove unused variables * Better name for native constants * use WeakSet instead of Set * when native request is openRTB, the whole native.ortb is passed over * retain some defaults dor native PBS request * fix for empty ortbRequest in nativeBidIsValid * add non-asset properties to media type object * final fixes after rebasing * handle tracking of soon-deprecated ortb imptrackers and jstracker * let native ad unit take all horizontal space * pass "mapping" from legacy native to ortb to prebid universal creative * Convert ortb assets to legacy for backward compatibility with legacy templates * add ortb conversion for more bid adapters * wrap conversion function in FEATURES.NATIVE * remove expired bids from native wrapper * instead of modifying bidresponse, use nativeReq * wrap test in FEATURES.NATIVE * fix for native mapping in prebidServerBidAdapter * use copy-on-write to convert ortb requests to legacy * fix comment on function * Update criteoBidAdapter.js Co-authored-by: Filip Stamenkovic Co-authored-by: Michele Nasti Co-authored-by: Patrick McCann --- .../gpt/prebidServer_native_example.html | 4 +- modules/ablidaBidAdapter.js | 4 + modules/adagioBidAdapter.js | 4 + modules/adbookpspBidAdapter.js | 4 + modules/adfBidAdapter.js | 4 + modules/adgenerationBidAdapter.js | 3 + modules/adkernelBidAdapter.js | 4 + modules/admanBidAdapter.js | 4 + modules/admixerBidAdapter.js | 4 + modules/adnowBidAdapter.js | 4 + modules/adotBidAdapter.js | 3 + modules/adprimeBidAdapter.js | 4 + modules/adrelevantisBidAdapter.js | 4 + modules/adrinoBidAdapter.js | 3 + modules/adtrueBidAdapter.js | 4 + modules/aduptechBidAdapter.js | 4 + modules/adxcgBidAdapter.js | 4 + modules/adyoulikeBidAdapter.js | 3 + modules/ajaBidAdapter.js | 4 + modules/appnexusBidAdapter.js | 4 + modules/apstreamBidAdapter.js | 3 + modules/bidscubeBidAdapter.js | 4 + modules/bizzclickBidAdapter.js | 4 + modules/boldwinBidAdapter.js | 4 + modules/braveBidAdapter.js | 4 + modules/bridgewellBidAdapter.js | 4 + modules/buzzoolaBidAdapter.js | 4 + modules/clickforceBidAdapter.js | 4 + modules/colossussspBidAdapter.js | 4 + modules/compassBidAdapter.js | 4 + modules/contentexchangeBidAdapter.js | 4 + modules/craftBidAdapter.js | 4 + modules/criteoBidAdapter.js | 4 + modules/dailyhuntBidAdapter.js | 4 + modules/datablocksBidAdapter.js | 4 + modules/dianomiBidAdapter.js | 3 + modules/e_volutionBidAdapter.js | 4 + modules/engageyaBidAdapter.js | 5 + modules/finativeBidAdapter.js | 3 + modules/goldbachBidAdapter.js | 4 + modules/gothamadsBidAdapter.js | 4 + modules/growadvertisingBidAdapter.js | 4 + modules/improvedigitalBidAdapter.js | 4 + modules/iqzoneBidAdapter.js | 4 + modules/ixBidAdapter.js | 3 + modules/krushmediaBidAdapter.js | 4 + modules/livewrappedBidAdapter.js | 4 + modules/loganBidAdapter.js | 4 + modules/logicadBidAdapter.js | 4 + modules/loglyliftBidAdapter.js | 4 + modules/lunamediahbBidAdapter.js | 4 + modules/mathildeadsBidAdapter.js | 4 + modules/mediaforceBidAdapter.js | 4 + modules/mediafuseBidAdapter.js | 3 + modules/mediakeysBidAdapter.js | 4 + modules/medianetBidAdapter.js | 4 + modules/mediasquareBidAdapter.js | 4 + modules/mgidBidAdapter.js | 4 + modules/microadBidAdapter.js | 4 + modules/mobfoxpbBidAdapter.js | 3 + modules/my6senseBidAdapter.js | 4 + modules/nextrollBidAdapter.js | 3 + modules/operaadsBidAdapter.js | 4 + modules/orbidderBidAdapter.js | 4 + modules/outbrainBidAdapter.js | 3 + modules/ozoneBidAdapter.js | 4 + modules/prebidServerBidAdapter/index.js | 129 ++-- modules/pubmaticBidAdapter.js | 3 + modules/pubwiseBidAdapter.js | 4 + modules/pulsepointBidAdapter.js | 4 + modules/readpeakBidAdapter.js | 4 + modules/revcontentBidAdapter.js | 4 + modules/rtbhouseBidAdapter.js | 4 + modules/seedingAllianceBidAdapter.js | 4 + modules/smarthubBidAdapter.js | 3 + modules/smartyadsBidAdapter.js | 4 + modules/sspBCBidAdapter.js | 4 + modules/talkadsBidAdapter.js | 3 + modules/temedyaBidAdapter.js | 4 + modules/theAdxBidAdapter.js | 4 + modules/trafficgateBidAdapter.js | 3 + modules/ucfunnelBidAdapter.js | 4 + modules/ventesBidAdapter.js | 4 + modules/vibrantmediaBidAdapter.js | 4 + modules/videoheroesBidAdapter.js | 3 + modules/yieldlabBidAdapter.js | 4 + src/constants.json | 52 +- src/native.js | 504 ++++++++++++++- src/prebid.js | 10 + src/secureCreatives.js | 17 +- .../modules/prebidServerBidAdapter_spec.js | 272 +++++--- test/spec/native_spec.js | 587 +++++++++++++++--- test/spec/unit/core/bidderFactory_spec.js | 10 +- test/spec/unit/pbjs_api_spec.js | 49 +- test/spec/unit/secureCreatives_spec.js | 94 +-- 95 files changed, 1652 insertions(+), 396 deletions(-) diff --git a/integrationExamples/gpt/prebidServer_native_example.html b/integrationExamples/gpt/prebidServer_native_example.html index 16c7d38a427..c590f0bcee5 100644 --- a/integrationExamples/gpt/prebidServer_native_example.html +++ b/integrationExamples/gpt/prebidServer_native_example.html @@ -133,8 +133,8 @@ ', + javascriptTrackers: '', ext: { foo: 'foo-value', - baz: 'baz-value' - } - } + baz: 'baz-value', + }, + }, }; const bidWithUndefinedFields = { @@ -50,12 +54,12 @@ const bidWithUndefinedFields = { clickUrl: 'https://www.link.example', clickTrackers: ['https://tracker.example'], impressionTrackers: ['https://impression.example'], - javascriptTrackers: '', + javascriptTrackers: '', ext: { foo: 'foo-value', - baz: undefined - } - } + baz: undefined, + }, + }, }; describe('native.js', function () { @@ -80,7 +84,9 @@ describe('native.js', function () { const targeting = getNativeTargeting(bid); expect(targeting[CONSTANTS.NATIVE_KEYS.title]).to.equal(bid.native.title); expect(targeting[CONSTANTS.NATIVE_KEYS.body]).to.equal(bid.native.body); - expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal(bid.native.clickUrl); + expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal( + bid.native.clickUrl + ); expect(targeting.hb_native_foo).to.equal(bid.native.foo); }); @@ -92,19 +98,23 @@ describe('native.js', function () { clickUrl: { sendId: true }, ext: { foo: { - sendId: false + sendId: false, }, baz: { - sendId: true - } - } - } + sendId: true, + }, + }, + }, }; const targeting = getNativeTargeting(bid, deps(adUnit)); expect(targeting[CONSTANTS.NATIVE_KEYS.title]).to.equal(bid.native.title); - expect(targeting[CONSTANTS.NATIVE_KEYS.body]).to.equal('hb_native_body:123'); - expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal('hb_native_linkurl:123'); + expect(targeting[CONSTANTS.NATIVE_KEYS.body]).to.equal( + 'hb_native_body:123' + ); + expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal( + 'hb_native_linkurl:123' + ); expect(targeting.hb_native_foo).to.equal(bid.native.ext.foo); expect(targeting.hb_native_baz).to.equal('hb_native_baz:123'); }); @@ -117,13 +127,13 @@ describe('native.js', function () { clickUrl: { sendId: true }, ext: { foo: { - required: false + required: false, }, baz: { - required: false - } - } - } + required: false, + }, + }, + }, }; const targeting = getNativeTargeting(bidWithUndefinedFields, deps(adUnit)); @@ -132,7 +142,7 @@ describe('native.js', function () { CONSTANTS.NATIVE_KEYS.title, CONSTANTS.NATIVE_KEYS.sponsoredBy, CONSTANTS.NATIVE_KEYS.clickUrl, - 'hb_native_foo' + 'hb_native_foo', ]); }); @@ -142,22 +152,19 @@ describe('native.js', function () { nativeParams: { image: { required: true, - sizes: [150, 50] + sizes: [150, 50], }, title: { required: true, len: 80, - sendTargetingKeys: true + sendTargetingKeys: true, }, sendTargetingKeys: false, - } - + }, }; const targeting = getNativeTargeting(bid, deps(adUnit)); - expect(Object.keys(targeting)).to.deep.equal([ - CONSTANTS.NATIVE_KEYS.title - ]); + expect(Object.keys(targeting)).to.deep.equal([CONSTANTS.NATIVE_KEYS.title]); }); it('should only include targeting if sendTargetingKeys not set to false', function () { @@ -166,38 +173,37 @@ describe('native.js', function () { nativeParams: { image: { required: true, - sizes: [150, 50] + sizes: [150, 50], }, title: { required: true, - len: 80 + len: 80, }, body: { - required: true + required: true, }, clickUrl: { - required: true + required: true, }, icon: { required: false, - sendTargetingKeys: false + sendTargetingKeys: false, }, cta: { required: false, - sendTargetingKeys: false + sendTargetingKeys: false, }, sponsoredBy: { required: false, - sendTargetingKeys: false + sendTargetingKeys: false, }, ext: { foo: { required: false, - sendTargetingKeys: true - } - } - } - + sendTargetingKeys: true, + }, + }, + }, }; const targeting = getNativeTargeting(bid, deps(adUnit)); @@ -206,7 +212,7 @@ describe('native.js', function () { CONSTANTS.NATIVE_KEYS.body, CONSTANTS.NATIVE_KEYS.image, CONSTANTS.NATIVE_KEYS.clickUrl, - 'hb_native_foo' + 'hb_native_foo', ]); }); @@ -216,17 +222,16 @@ describe('native.js', function () { nativeParams: { image: { required: true, - sizes: [150, 50] + sizes: [150, 50], }, title: { required: true, len: 80, }, rendererUrl: { - url: 'https://www.renderer.com/' - } - } - + url: 'https://www.renderer.com/', + }, + }, }; const targeting = getNativeTargeting(bid, deps(adUnit)); @@ -238,7 +243,7 @@ describe('native.js', function () { CONSTANTS.NATIVE_KEYS.icon, CONSTANTS.NATIVE_KEYS.sponsoredBy, CONSTANTS.NATIVE_KEYS.clickUrl, - CONSTANTS.NATIVE_KEYS.rendererUrl + CONSTANTS.NATIVE_KEYS.rendererUrl, ]); expect(bid.native.rendererUrl).to.deep.equal('https://www.renderer.com/'); @@ -251,15 +256,14 @@ describe('native.js', function () { nativeParams: { image: { required: true, - sizes: [150, 50] + sizes: [150, 50], }, title: { required: true, len: 80, }, - adTemplate: '

##hb_native_body##<\/p><\/div>' - } - + adTemplate: '

##hb_native_body##

', + }, }; const targeting = getNativeTargeting(bid, deps(adUnit)); @@ -270,10 +274,12 @@ describe('native.js', function () { CONSTANTS.NATIVE_KEYS.image, CONSTANTS.NATIVE_KEYS.icon, CONSTANTS.NATIVE_KEYS.sponsoredBy, - CONSTANTS.NATIVE_KEYS.clickUrl + CONSTANTS.NATIVE_KEYS.clickUrl, ]); - expect(bid.native.adTemplate).to.deep.equal('

##hb_native_body##<\/p><\/div>'); + expect(bid.native.adTemplate).to.deep.equal( + '

##hb_native_body##

' + ); delete bid.native.adTemplate; }); @@ -281,7 +287,10 @@ describe('native.js', function () { fireNativeTrackers({}, bid); sinon.assert.calledOnce(triggerPixelStub); sinon.assert.calledWith(triggerPixelStub, bid.native.impressionTrackers[0]); - sinon.assert.calledWith(insertHtmlIntoIframeStub, bid.native.javascriptTrackers); + sinon.assert.calledWith( + insertHtmlIntoIframeStub, + bid.native.javascriptTrackers + ); }); it('fires click trackers', function () { @@ -291,7 +300,7 @@ describe('native.js', function () { sinon.assert.calledWith(triggerPixelStub, bid.native.clickTrackers[0]); }); - it('creates native asset message', function() { + it('creates native asset message', function () { const messageRequest = { message: 'Prebid Native', action: 'assetRequest', @@ -304,19 +313,19 @@ describe('native.js', function () { expect(message.assets.length).to.equal(3); expect(message.assets).to.deep.include({ key: 'body', - value: bid.native.body + value: bid.native.body, }); expect(message.assets).to.deep.include({ key: 'image', - value: bid.native.image.url + value: bid.native.image.url, }); expect(message.assets).to.deep.include({ key: 'clickUrl', - value: bid.native.clickUrl + value: bid.native.clickUrl, }); }); - it('creates native all asset message', function() { + it('creates native all asset message', function () { const messageRequest = { message: 'Prebid Native', action: 'allAssetRequest', @@ -328,43 +337,43 @@ describe('native.js', function () { expect(message.assets.length).to.equal(9); expect(message.assets).to.deep.include({ key: 'body', - value: bid.native.body + value: bid.native.body, }); expect(message.assets).to.deep.include({ key: 'image', - value: bid.native.image.url + value: bid.native.image.url, }); expect(message.assets).to.deep.include({ key: 'clickUrl', - value: bid.native.clickUrl + value: bid.native.clickUrl, }); expect(message.assets).to.deep.include({ key: 'title', - value: bid.native.title + value: bid.native.title, }); expect(message.assets).to.deep.include({ key: 'icon', - value: bid.native.icon.url + value: bid.native.icon.url, }); expect(message.assets).to.deep.include({ key: 'cta', - value: bid.native.cta + value: bid.native.cta, }); expect(message.assets).to.deep.include({ key: 'sponsoredBy', - value: bid.native.sponsoredBy + value: bid.native.sponsoredBy, }); expect(message.assets).to.deep.include({ key: 'foo', - value: bid.native.ext.foo + value: bid.native.ext.foo, }); expect(message.assets).to.deep.include({ key: 'baz', - value: bid.native.ext.baz + value: bid.native.ext.baz, }); }); - it('creates native all asset message with only defined fields', function() { + it('creates native all asset message with only defined fields', function () { const messageRequest = { message: 'Prebid Native', action: 'allAssetRequest', @@ -376,23 +385,97 @@ describe('native.js', function () { expect(message.assets.length).to.equal(4); expect(message.assets).to.deep.include({ key: 'clickUrl', - value: bid.native.clickUrl + value: bid.native.clickUrl, }); expect(message.assets).to.deep.include({ key: 'title', - value: bid.native.title + value: bid.native.title, }); expect(message.assets).to.deep.include({ key: 'sponsoredBy', - value: bid.native.sponsoredBy + value: bid.native.sponsoredBy, }); expect(message.assets).to.deep.include({ key: 'foo', - value: bid.native.ext.foo + value: bid.native.ext.foo, }); }); }); +describe('validate native openRTB', function () { + it('should validate openRTB request', function () { + let openRTBNativeRequest = { assets: [] }; + // assets array can't be empty + expect(isOpenRTBBidRequestValid(openRTBNativeRequest)).to.eq(false); + openRTBNativeRequest.assets.push({ + id: 1.5, + required: 1, + title: {}, + }); + + // asset.id must be integer + expect(isOpenRTBBidRequestValid(openRTBNativeRequest)).to.eq(false); + openRTBNativeRequest.assets[0].id = 1; + // title must have 'len' property + expect(isOpenRTBBidRequestValid(openRTBNativeRequest)).to.eq(false); + openRTBNativeRequest.assets[0].title.len = 140; + // openRTB request is valid + expect(isOpenRTBBidRequestValid(openRTBNativeRequest)).to.eq(true); + + openRTBNativeRequest.assets.push({ + id: 2, + required: 1, + video: { + mimes: [], + protocols: [], + minduration: 50, + }, + }); + // video asset should have all required properties + expect(isOpenRTBBidRequestValid(openRTBNativeRequest)).to.eq(false); + openRTBNativeRequest.assets[1].video.maxduration = 60; + expect(isOpenRTBBidRequestValid(openRTBNativeRequest)).to.eq(true); + }); + + it('should validate openRTB native bid', function () { + const openRTBRequest = { + assets: [ + { + id: 1, + required: 1, + }, + { + id: 2, + required: 0, + }, + { + id: 3, + required: 1, + }, + ], + }; + let openRTBBid = { + assets: [ + { + id: 1, + }, + { + id: 2, + }, + ], + }; + + // link is missing + expect(isNativeOpenRTBBidValid(openRTBBid, openRTBRequest)).to.eq(false); + openRTBBid.link = { url: 'www.foo.bar' }; + // required id == 3 is missing + expect(isNativeOpenRTBBidValid(openRTBBid, openRTBRequest)).to.eq(false); + + openRTBBid.assets[1].id = 3; + expect(isNativeOpenRTBBidValid(openRTBBid, openRTBRequest)).to.eq(true); + }); +}); + describe('validate native', function () { const adUnit = { transactionId: 'test_adunit', @@ -407,15 +490,15 @@ describe('validate native', function () { image: { required: true, sizes: [150, 50], - aspect_ratios: [150, 50] + aspect_ratios: [150, 50], }, icon: { required: true, - sizes: [50, 50] + sizes: [50, 50], }, - } - } - } + }, + }, + }; let validBid = { adId: 'abc123', @@ -424,23 +507,24 @@ describe('validate native', function () { adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { - body: 'This is a Prebid Native Creative. There are many like it, but this one is mine.', + body: + 'This is a Prebid Native Creative. There are many like it, but this one is mine.', clickTrackers: ['http://my.click.tracker/url'], icon: { url: 'http://my.image.file/ad_image.jpg', height: 75, - width: 75 + width: 75, }, image: { url: 'http://my.icon.file/ad_icon.jpg', height: 2250, - width: 3000 + width: 3000, }, clickUrl: 'http://prebid.org/dev-docs/show-native-ads.html', impressionTrackers: ['http://my.imp.tracker/url'], - javascriptTrackers: '', - title: 'This is an example Prebid Native creative' - } + javascriptTrackers: '', + title: 'This is an example Prebid Native creative', + }, }; let noIconDimBid = { @@ -450,19 +534,20 @@ describe('validate native', function () { adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { - body: 'This is a Prebid Native Creative. There are many like it, but this one is mine.', + body: + 'This is a Prebid Native Creative. There are many like it, but this one is mine.', clickTrackers: ['http://my.click.tracker/url'], icon: 'http://my.image.file/ad_image.jpg', image: { url: 'http://my.icon.file/ad_icon.jpg', height: 2250, - width: 3000 + width: 3000, }, clickUrl: 'http://prebid.org/dev-docs/show-native-ads.html', impressionTrackers: ['http://my.imp.tracker/url'], - javascriptTrackers: '', - title: 'This is an example Prebid Native creative' - } + javascriptTrackers: '', + title: 'This is an example Prebid Native creative', + }, }; let noImgDimBid = { @@ -472,19 +557,20 @@ describe('validate native', function () { adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { - body: 'This is a Prebid Native Creative. There are many like it, but this one is mine.', + body: + 'This is a Prebid Native Creative. There are many like it, but this one is mine.', clickTrackers: ['http://my.click.tracker/url'], icon: { url: 'http://my.image.file/ad_image.jpg', height: 75, - width: 75 + width: 75, }, image: 'http://my.icon.file/ad_icon.jpg', clickUrl: 'http://prebid.org/dev-docs/show-native-ads.html', impressionTrackers: ['http://my.imp.tracker/url'], - javascriptTrackers: '', - title: 'This is an example Prebid Native creative' - } + javascriptTrackers: '', + title: 'This is an example Prebid Native creative', + }, }; beforeEach(function () {}); @@ -493,12 +579,307 @@ describe('validate native', function () { it('should accept bid if no image sizes are defined', function () { decorateAdUnitsWithNativeParams([adUnit]); - const index = stubAuctionIndex({adUnits: [adUnit]}) - let result = nativeBidIsValid(validBid, {index}); + const index = stubAuctionIndex({ adUnits: [adUnit] }); + let result = nativeBidIsValid(validBid, { index }); expect(result).to.be.true; - result = nativeBidIsValid(noIconDimBid, {index}); + result = nativeBidIsValid(noIconDimBid, { index }); expect(result).to.be.true; - result = nativeBidIsValid(noImgDimBid, {index}); + result = nativeBidIsValid(noImgDimBid, { index }); expect(result).to.be.true; }); + + it('should convert from old-style native to OpenRTB request', () => { + const adUnit = { + transactionId: 'test_adunit', + mediaTypes: { + native: { + title: { + required: true, + }, + body: { + required: true, + len: 45 + }, + image: { + required: true, + sizes: [150, 50], + aspect_ratios: [{ + min_width: 150, + min_height: 50 + }] + }, + icon: { + required: true, + aspect_ratios: [{ + min_width: 150, + min_height: 50 + }] + }, + address: {}, + }, + }, + }; + + const ortb = toOrtbNativeRequest(adUnit.mediaTypes.native); + expect(ortb).to.be.a('object'); + expect(ortb.assets).to.be.a('array'); + + // title + expect(ortb.assets[0]).to.deep.include({ + id: 0, + required: 1, + title: { + len: 140 + } + }); + + // body => data + expect(ortb.assets[1]).to.deep.include({ + id: 1, + required: 1, + data: { + type: 2, + len: 45 + } + }); + + // image => image + expect(ortb.assets[2]).to.deep.include({ + id: 2, + required: 1, + img: { + type: 3, // Main Image + w: 150, + h: 50, + } + }); + + expect(ortb.assets[3]).to.deep.include({ + id: 3, + required: 1, + img: { + type: 1, // Icon Image + wmin: 150, + hmin: 50, + } + }); + + expect(ortb.assets[4]).to.deep.include({ + id: 4, + required: 0, + data: { + type: 9, + } + }); + }); + + it('should convert from ortb to old-style native request', () => { + const openRTBRequest = { + 'ver': '1.2', + 'context': 2, + 'contextsubtype': 20, + 'plcmttype': 11, + 'plcmtcnt': 1, + 'aurlsupport': 0, + 'privacy': 1, + 'eventrackers': [ + { + 'event': 1, + 'methods': [1, 2] + }, + { + 'event': 2, + 'methods': [1] + } + ], + 'assets': [ + { + 'id': 123, + 'required': 1, + 'title': { + 'len': 140 + } + }, + { + 'id': 128, + 'required': 0, + 'img': { + 'wmin': 836, + 'hmin': 627, + 'type': 3 + } + }, + { + 'id': 124, + 'required': 1, + 'img': { + 'wmin': 50, + 'hmin': 50, + 'type': 1 + } + }, + { + 'id': 126, + 'required': 1, + 'data': { + 'type': 1, + 'len': 25 + } + }, + { + 'id': 127, + 'required': 1, + 'data': { + 'type': 2, + 'len': 140 + } + } + ] + }; + + const oldNativeRequest = fromOrtbNativeRequest(openRTBRequest); + + expect(oldNativeRequest).to.be.a('object'); + expect(oldNativeRequest.title).to.include({ + required: true, + len: 140 + }); + + expect(oldNativeRequest.image).to.deep.include({ + required: false, + aspect_ratios: { + min_width: 836, + min_height: 627, + ratio_width: 836, + ratio_height: 627 + } + }); + + expect(oldNativeRequest.icon).to.deep.include({ + required: true, + aspect_ratios: { + min_width: 50, + min_height: 50, + ratio_width: 50, + ratio_height: 50 + } + }); + expect(oldNativeRequest.sponsoredBy).to.include({ + required: true, + len: 25 + }) + expect(oldNativeRequest.body).to.include({ + required: true, + len: 140 + }) + }); + + if (FEATURES.NATIVE) { + it('should convert ortb bid requests to proprietary requests', () => { + const validBidRequests = [{ + bidId: 'bidId3', + adUnitCode: 'adUnitCode3', + transactionId: 'transactionId3', + mediaTypes: { + banner: {} + }, + params: { + publisher: 'publisher2', + placement: 'placement3' + } + }]; + const resultRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + expect(resultRequests).to.be.deep.equals(validBidRequests); + + validBidRequests[0].mediaTypes.native = { + ortb: { + ver: '1.2', + context: 2, + contextsubtype: 20, + plcmttype: 11, + plcmtcnt: 1, + aurlsupport: 0, + privacy: 1, + eventrackers: [ + { + event: 1, + methods: [1, 2] + }, + { + event: 2, + methods: [1] + } + ], + assets: [ + { + id: 123, + required: 1, + title: { + len: 140 + } + }, + { + id: 128, + required: 0, + img: { + wmin: 836, + hmin: 627, + type: 3 + } + }, + { + id: 124, + required: 1, + img: { + wmin: 50, + hmin: 50, + type: 1 + } + }, + { + id: 126, + required: 1, + data: { + type: 1, + len: 25 + } + }, + { + id: 127, + required: 1, + data: { + type: 2, + len: 140 + } + } + ] + } + }; + + const resultRequests2 = convertOrtbRequestToProprietaryNative(validBidRequests); + expect(resultRequests2[0].mediaTypes.native).to.deep.include({ + title: { + required: true, + len: 140 + }, + icon: { + required: true, + aspect_ratios: { + min_width: 50, + min_height: 50, + ratio_width: 50, + ratio_height: 50 + } + }, + sponsoredBy: { + required: true, + len: 25 + }, + body: { + required: true, + len: 140 + } + }); + }); + } }); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index be68fc03765..646791c7e1f 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -39,6 +39,10 @@ function onTimelyResponseStub() { } +before(() => { + hook.ready(); +}); + let wrappedCallback = config.callbackWithBidder(CODE); describe('bidders created by newBidder', function () { @@ -47,10 +51,6 @@ describe('bidders created by newBidder', function () { let addBidResponseStub; let doneStub; - before(() => { - hook.ready(); - }); - beforeEach(function () { spec = { code: CODE, @@ -952,7 +952,7 @@ describe('validate bid response: ', function () { bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.calledOnce).to.equal(false); - expect(logErrorSpy.callCount).to.equal(1); + expect(logErrorSpy.calledWithMatch('Ignoring bid: Native bid missing some required properties.')).to.equal(true); }); } diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 5c0391f96bf..3cee2b6b679 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -2376,14 +2376,47 @@ describe('Unit: Prebid Module', function () { $$PREBID_GLOBAL$$.requestBids({adUnits}); const spyArgs = adapterManager.callBids.getCall(0); const nativeRequest = spyArgs.args[1][0].bids[0].nativeParams; - expect(nativeRequest).to.deep.equal({ - image: {required: true}, - title: {required: true}, - sponsoredBy: {required: true}, - clickUrl: {required: true}, - body: {required: false}, - icon: {required: false}, - }); + expect(nativeRequest.ortb.assets).to.deep.equal([ + { + required: 1, + id: 1, + img: { + type: 3, + wmin: 100, + hmin: 100, + } + }, + { + required: 1, + id: 2, + title: { + len: 140, + } + }, + { + required: 1, + id: 3, + data: { + type: 1, + } + }, + { + required: 0, + id: 4, + data: { + type: 2, + } + }, + { + required: 0, + id: 5, + img: { + type: 1, + wmin: 20, + hmin: 20, + } + }, + ]); resetAuction(); }); }); diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js index 39fa9b9250f..7d5f9af35dd 100644 --- a/test/spec/unit/secureCreatives_spec.js +++ b/test/spec/unit/secureCreatives_spec.js @@ -1,7 +1,6 @@ import { _sendAdToCreative, getReplier, receiveMessage } from 'src/secureCreatives.js'; -import * as secureCreatives from 'src/secureCreatives.js'; import * as utils from 'src/utils.js'; import {getAdUnits, getBidRequests, getBidResponses} from 'test/fixtures/fixtures.js'; import {auctionManager} from 'src/auctionManager.js'; @@ -164,6 +163,7 @@ describe('secureCreatives', () => { stubGetAllAssetsMessage.restore(); stubEmit.restore(); resetAuction(); + adResponse.adId = bidId; }); describe('Prebid Request', function() { @@ -336,60 +336,17 @@ describe('secureCreatives', () => { sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); sinon.assert.calledOnce(ev.source.postMessage); sinon.assert.notCalled(stubFireNativeTrackers); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); - sinon.assert.notCalled(spyAddWinningBid); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); - }); - - it('Prebid native should allow stale rendering without config', function () { - pushBidResponseToAuction({}); - - const data = { - adId: bidId, - message: 'Prebid Native', - action: 'allAssetRequest' - }; - - const ev = makeEvent({ - data: JSON.stringify(data), - source: { - postMessage: sinon.stub() - }, - origin: 'any origin' - }); - - receiveMessage(ev); - - sinon.assert.neverCalledWith(spyLogWarn, warning); - sinon.assert.calledOnce(stubGetAllAssetsMessage); - sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); - sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.notCalled(stubFireNativeTrackers); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); - sinon.assert.notCalled(spyAddWinningBid); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); - - resetHistories(ev.source.postMessage); - - receiveMessage(ev); - - sinon.assert.neverCalledWith(spyLogWarn, warning); - sinon.assert.calledOnce(stubGetAllAssetsMessage); - sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); - sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.notCalled(stubFireNativeTrackers); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); - sinon.assert.notCalled(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); }); - it('Prebid native should allow stale rendering with config', function () { - configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); - - pushBidResponseToAuction({}); + it('Prebid native should not fire BID_WON when receiveMessage is called more than once', () => { + let adId = 3; + pushBidResponseToAuction({ adId }); const data = { - adId: bidId, + adId: adId, message: 'Prebid Native', action: 'allAssetRequest' }; @@ -403,37 +360,18 @@ describe('secureCreatives', () => { }); receiveMessage(ev); - - sinon.assert.neverCalledWith(spyLogWarn, warning); - sinon.assert.calledOnce(stubGetAllAssetsMessage); - sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); - sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.notCalled(stubFireNativeTrackers); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); - sinon.assert.notCalled(spyAddWinningBid); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); - - resetHistories(ev.source.postMessage); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); receiveMessage(ev); - - sinon.assert.neverCalledWith(spyLogWarn, warning); - sinon.assert.calledOnce(stubGetAllAssetsMessage); - sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); - sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.notCalled(stubFireNativeTrackers); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); - sinon.assert.notCalled(spyAddWinningBid); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); - - configObj.setConfig({'auctionOptions': {}}); + stubEmit.withArgs(CONSTANTS.EVENTS.BID_WON, adResponse).calledOnce; }); it('Prebid native should fire trackers', function () { - pushBidResponseToAuction({}); + let adId = 2; + pushBidResponseToAuction({adId}); const data = { - adId: bidId, + adId: adId, message: 'Prebid Native', action: 'click', }; @@ -450,8 +388,8 @@ describe('secureCreatives', () => { sinon.assert.neverCalledWith(spyLogWarn, warning); sinon.assert.calledOnce(stubFireNativeTrackers); - sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); - sinon.assert.notCalled(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); resetHistories(ev.source.postMessage); @@ -461,8 +399,8 @@ describe('secureCreatives', () => { sinon.assert.neverCalledWith(spyLogWarn, warning); sinon.assert.calledOnce(stubFireNativeTrackers); - sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); - sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); + sinon.assert.notCalled(spyAddWinningBid); expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); }); From 90d1a188860a8ec256a0db2f660bbe9d8a46b655 Mon Sep 17 00:00:00 2001 From: Andrew Slagle <42588549+spotxslagle@users.noreply.github.com> Date: Tue, 26 Jul 2022 11:44:48 -0600 Subject: [PATCH 076/569] Spotx Adapter: Remove append child (#8739) * Allow SpotX module to load script without appendChild * Fix SpotX spec to no longer use appendChild * Revert adloader change * Fix test merge issues --- modules/spotxBidAdapter.js | 79 +++++------ test/spec/modules/spotxBidAdapter_spec.js | 163 ++++++++++------------ 2 files changed, 114 insertions(+), 128 deletions(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index bcc65c85791..d044df74cfe 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -1,8 +1,9 @@ -import { logError, deepAccess, isArray, getBidIdParameter, getDNT, deepSetValue, isEmpty, _each, logMessage, logWarn, isBoolean, isNumber, isPlainObject, isFn } from '../src/utils.js'; +import { logError, deepAccess, isArray, getBidIdParameter, getDNT, deepSetValue, isEmpty, _each, logMessage, logWarn, isBoolean, isNumber, isPlainObject, isFn, setScriptAttributes } from '../src/utils.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; +import { loadExternalScript } from '../src/adloader.js'; const BIDDER_CODE = 'spotx'; const URL = 'https://search.spotxchange.com/openrtb/2.3/dados/'; @@ -421,11 +422,45 @@ export const spec = { } function createOutstreamScript(bid) { - const slot = getBidIdParameter('slot', bid.renderer.config.outstream_options); - logMessage('[SPOTX][renderer] Handle SpotX outstream renderer'); const script = window.document.createElement('script'); + let dataSpotXParams = createScriptAttributeMap(bid); + script.type = 'text/javascript'; script.src = 'https://js.spotx.tv/easi/v1/' + bid.channel_id + '.js'; + + setScriptAttributes(script, dataSpotXParams); + + return script; +} + +function outstreamRender(bid) { + if (bid.renderer.config.outstream_function != null && typeof bid.renderer.config.outstream_function === 'function') { + const script = createOutstreamScript(bid); + bid.renderer.config.outstream_function(bid, script); + } else { + try { + const inIframe = getBidIdParameter('in_iframe', bid.renderer.config.outstream_options); + const easiUrl = 'https://js.spotx.tv/easi/v1/' + bid.channel_id + '.js'; + let attributes = createScriptAttributeMap(bid); + if (inIframe && window.document.getElementById(inIframe).nodeName == 'IFRAME') { + const rawframe = window.document.getElementById(inIframe); + let framedoc = rawframe.contentDocument; + if (!framedoc && rawframe.contentWindow) { + framedoc = rawframe.contentWindow.document; + } + loadExternalScript(easiUrl, BIDDER_CODE, undefined, framedoc, attributes); + } else { + loadExternalScript(easiUrl, BIDDER_CODE, undefined, undefined, attributes); + } + } catch (err) { + logError('[SPOTX][renderer] Error:' + err.message) + } + } +} + +function createScriptAttributeMap(bid) { + const slot = getBidIdParameter('slot', bid.renderer.config.outstream_options); + logMessage('[SPOTX][renderer] Handle SpotX outstream renderer'); let dataSpotXParams = {}; dataSpotXParams['data-spotx_channel_id'] = '' + bid.channel_id; dataSpotXParams['data-spotx_vast_url'] = '' + bid.vastUrl; @@ -440,6 +475,7 @@ function createOutstreamScript(bid) { dataSpotXParams['data-spotx_autoplay'] = '1'; dataSpotXParams['data-spotx_blocked_autoplay_override_mode'] = '1'; dataSpotXParams['data-spotx_video_slot_can_autoplay'] = '1'; + dataSpotXParams['data-spotx_content_container_id'] = slot; const playersizeAutoAdapt = getBidIdParameter('playersize_auto_adapt', bid.renderer.config.outstream_options); if (playersizeAutoAdapt && isBoolean(playersizeAutoAdapt) && playersizeAutoAdapt === true) { @@ -478,42 +514,7 @@ function createOutstreamScript(bid) { } } } - - for (let key in dataSpotXParams) { - if (dataSpotXParams.hasOwnProperty(key)) { - script.setAttribute(key, dataSpotXParams[key]); - } - } - - return script; -} - -function outstreamRender(bid) { - const script = createOutstreamScript(bid); - if (bid.renderer.config.outstream_function != null && typeof bid.renderer.config.outstream_function === 'function') { - bid.renderer.config.outstream_function(bid, script); - } else { - try { - const inIframe = getBidIdParameter('in_iframe', bid.renderer.config.outstream_options); - if (inIframe && window.document.getElementById(inIframe).nodeName == 'IFRAME') { - const rawframe = window.document.getElementById(inIframe); - let framedoc = rawframe.contentDocument; - if (!framedoc && rawframe.contentWindow) { - framedoc = rawframe.contentWindow.document; - } - framedoc.body.appendChild(script); - } else { - const slot = getBidIdParameter('slot', bid.renderer.config.outstream_options); - if (slot && window.document.getElementById(slot)) { - window.document.getElementById(slot).appendChild(script); - } else { - window.document.getElementsByTagName('head')[0].appendChild(script); - } - } - } catch (err) { - logError('[SPOTX][renderer] Error:' + err.message) - } - } + return dataSpotXParams; } registerBidder(spec); diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index 274ef60471b..74eaf669bce 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -1,5 +1,6 @@ import {expect} from 'chai'; import {config} from 'src/config.js'; +import {loadExternalScript} from '../../../src/adloader'; import {spec, GOOGLE_CONSENT} from 'modules/spotxBidAdapter.js'; describe('the spotx adapter', function () { @@ -18,14 +19,14 @@ describe('the spotx adapter', function () { }; describe('isBidRequestValid', function() { - var bid; + let bid; beforeEach(function() { bid = getValidBidObject(); }); it('should fail validation if the bid isn\'t defined or not an object', function() { - var result = spec.isBidRequestValid(); + let result = spec.isBidRequestValid(); expect(result).to.equal(false); @@ -92,7 +93,7 @@ describe('the spotx adapter', function () { }); describe('buildRequests', function() { - var bid, bidRequestObj; + let bid, bidRequestObj; beforeEach(function() { bid = getValidBidObject(); @@ -104,7 +105,7 @@ describe('the spotx adapter', function () { }); it('should build a very basic request', function() { - var request = spec.buildRequests([bid], bidRequestObj)[0]; + let request = spec.buildRequests([bid], bidRequestObj)[0]; expect(request.method).to.equal('POST'); expect(request.url).to.equal('https://search.spotxchange.com/openrtb/2.3/dados/12345?src_sys=prebid'); expect(request.bidRequest).to.equal(bidRequestObj); @@ -133,7 +134,7 @@ describe('the spotx adapter', function () { }); it('should change request parameters based on options sent', function() { - var request = spec.buildRequests([bid], bidRequestObj)[0]; + let request = spec.buildRequests([bid], bidRequestObj)[0]; expect(request.data.imp.video.ext).to.deep.equal({ sdk_name: 'Prebid 1+', versionOrtb: '2.3' @@ -239,7 +240,7 @@ describe('the spotx adapter', function () { }); it('should process premarket bids', function() { - var request; + let request; sinon.stub(Date, 'now').returns(1000); bid.params.pre_market_bids = [{ @@ -277,7 +278,7 @@ describe('the spotx adapter', function () { }); it('should pass GDPR params', function() { - var request; + let request; bidRequestObj.gdprConsent = { consentString: 'consent123', @@ -291,7 +292,7 @@ describe('the spotx adapter', function () { }); it('should pass CCPA us_privacy string', function() { - var request; + let request; bidRequestObj.uspConsent = '1YYY' @@ -300,7 +301,7 @@ describe('the spotx adapter', function () { }); it('should pass both GDPR params and CCPA us_privacy', function() { - var request; + let request; bidRequestObj.gdprConsent = { consentString: 'consent123', @@ -315,7 +316,7 @@ describe('the spotx adapter', function () { }); it('should pass min and max duration params', function() { - var request; + let request; bid.params.min_duration = 3 bid.params.max_duration = 15 @@ -327,7 +328,7 @@ describe('the spotx adapter', function () { }); it('should pass placement_type and position params', function() { - var request; + let request; bid.params.placement_type = 2 bid.params.position = 5 @@ -339,11 +340,11 @@ describe('the spotx adapter', function () { }); it('should pass page param and override refererInfo.referer', function() { - var request; + let request; bid.params.page = 'https://example.com'; - var origGetConfig = config.getConfig; + let origGetConfig = config.getConfig; sinon.stub(config, 'getConfig').callsFake(function (key) { if (key === 'pageUrl') { return 'https://www.spotx.tv'; @@ -358,7 +359,7 @@ describe('the spotx adapter', function () { }); it('should use refererInfo.referer if no page is passed', function() { - var request; + let request; request = spec.buildRequests([bid], bidRequestObj)[0]; @@ -366,9 +367,9 @@ describe('the spotx adapter', function () { }); it('should set ext.wrap_response to 0 when cache url is set and ignoreBidderCacheKey is true', function() { - var request; + let request; - var origGetConfig = config.getConfig; + let origGetConfig = config.getConfig; sinon.stub(config, 'getConfig').callsFake(function (key) { if (key === 'cache') { return { @@ -392,7 +393,7 @@ describe('the spotx adapter', function () { }); it('should pass price floor in USD from the floors module if available', function () { - var request; + let request; bid.getFloor = function () { return { currency: 'USD', floor: 3 }; @@ -406,7 +407,7 @@ describe('the spotx adapter', function () { }); it('should not pass price floor if price floors module gives a non-USD currency', function () { - var request; + let request; bid.getFloor = function () { return { currency: 'EUR', floor: 3 }; @@ -418,7 +419,7 @@ describe('the spotx adapter', function () { }); it('if floors module is not available, should pass price floor from price_floor param if available', function () { - var request; + let request; bid.params.price_floor = 2; @@ -429,7 +430,7 @@ describe('the spotx adapter', function () { }); describe('interpretResponse', function() { - var serverResponse, bidderRequestObj; + let serverResponse, bidderRequestObj; beforeEach(function() { bidderRequestObj = { @@ -503,7 +504,7 @@ describe('the spotx adapter', function () { }); it('should return an array of bid responses', function() { - var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + let responses = spec.interpretResponse(serverResponse, bidderRequestObj); expect(responses).to.be.an('array').with.length(2); expect(responses[0].cache_key).to.equal('cache123'); expect(responses[0].channel_id).to.equal(12345); @@ -548,9 +549,16 @@ describe('the spotx adapter', function () { }); describe('outstreamRender', function() { - var serverResponse, bidderRequestObj; + let serverResponse, bidderRequestObj; beforeEach(function() { + sinon.stub(window.document, 'getElementById').returns({ + clientWidth: 200, + appendChild: sinon.stub().callsFake(function(script) {}) + }); + sinon.stub(window.document, 'createElement').returns({ + setAttribute: function () {} + }); bidderRequestObj = { bidRequest: { bids: [{ @@ -600,99 +608,76 @@ describe('the spotx adapter', function () { } }; }); + afterEach(function () { + window.document.getElementById.restore(); + window.document.createElement.restore(); + }); it('should attempt to insert the EASI script', function() { - var scriptTag; + window.document.getElementById.restore(); sinon.stub(window.document, 'getElementById').returns({ - appendChild: sinon.stub().callsFake(function(script) { scriptTag = script; }) + appendChild: sinon.stub().callsFake(function(script) {}), }); - var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + let responses = spec.interpretResponse(serverResponse, bidderRequestObj); + let attrs; responses[0].renderer.render(responses[0]); - - expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); - expect(scriptTag.getAttribute('src')).to.equal('https://js.spotx.tv/easi/v1/12345.js'); - expect(scriptTag.getAttribute('data-spotx_channel_id')).to.equal('12345'); - expect(scriptTag.getAttribute('data-spotx_vast_url')).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache123'); - expect(scriptTag.getAttribute('data-spotx_ad_unit')).to.equal('incontent'); - expect(scriptTag.getAttribute('data-spotx_collapse')).to.equal('0'); - expect(scriptTag.getAttribute('data-spotx_autoplay')).to.equal('1'); - expect(scriptTag.getAttribute('data-spotx_blocked_autoplay_override_mode')).to.equal('1'); - expect(scriptTag.getAttribute('data-spotx_video_slot_can_autoplay')).to.equal('1'); - expect(scriptTag.getAttribute('data-spotx_digitrust_opt_out')).to.equal('1'); - expect(scriptTag.getAttribute('data-spotx_content_width')).to.equal('400'); - expect(scriptTag.getAttribute('data-spotx_content_height')).to.equal('300'); - expect(scriptTag.getAttribute('data-spotx_ad_mute')).to.equal('1'); - window.document.getElementById.restore(); + expect(loadExternalScript.called).to.be.true; + attrs = valuesToString(loadExternalScript.args[0][4]); + + expect(attrs['data-spotx_channel_id']).to.equal('12345'); + expect(attrs['data-spotx_vast_url']).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache123'); + expect(attrs['data-spotx_ad_unit']).to.equal('incontent'); + expect(attrs['data-spotx_collapse']).to.equal('0'); + expect(attrs['data-spotx_autoplay']).to.equal('1'); + expect(attrs['data-spotx_blocked_autoplay_override_mode']).to.equal('1'); + expect(attrs['data-spotx_video_slot_can_autoplay']).to.equal('1'); + expect(attrs['data-spotx_digitrust_opt_out']).to.equal('1'); + expect(attrs['data-spotx_content_width']).to.equal('400'); + expect(attrs['data-spotx_content_height']).to.equal('300'); + expect(attrs['data-spotx_ad_mute']).to.equal('1'); }); it('should append into an iframe', function() { - var scriptTag; + bidderRequestObj.bidRequest.bids[0].params.outstream_options.in_iframe = 'iframeId'; + window.document.getElementById.restore(); sinon.stub(window.document, 'getElementById').returns({ nodeName: 'IFRAME', - contentDocument: { - body: { - appendChild: sinon.stub().callsFake(function(script) { scriptTag = script; }) - } - } + clientWidth: 200, + appendChild: sinon.stub().callsFake(function(script) {}), + contentDocument: {nodeName: 'IFRAME'} }); - bidderRequestObj.bidRequest.bids[0].params.outstream_options.in_iframe = 'iframeId'; - - var responses = spec.interpretResponse(serverResponse, bidderRequestObj); - + let responses = spec.interpretResponse(serverResponse, bidderRequestObj); responses[0].renderer.render(responses[0]); - - expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); - expect(scriptTag.getAttribute('src')).to.equal('https://js.spotx.tv/easi/v1/12345.js'); - expect(scriptTag.getAttribute('data-spotx_channel_id')).to.equal('12345'); - expect(scriptTag.getAttribute('data-spotx_vast_url')).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache123'); - expect(scriptTag.getAttribute('data-spotx_ad_unit')).to.equal('incontent'); - expect(scriptTag.getAttribute('data-spotx_collapse')).to.equal('0'); - expect(scriptTag.getAttribute('data-spotx_autoplay')).to.equal('1'); - expect(scriptTag.getAttribute('data-spotx_blocked_autoplay_override_mode')).to.equal('1'); - expect(scriptTag.getAttribute('data-spotx_video_slot_can_autoplay')).to.equal('1'); - expect(scriptTag.getAttribute('data-spotx_digitrust_opt_out')).to.equal('1'); - expect(scriptTag.getAttribute('data-spotx_content_width')).to.equal('400'); - expect(scriptTag.getAttribute('data-spotx_content_height')).to.equal('300'); - window.document.getElementById.restore(); + expect(loadExternalScript.called).to.be.true; + expect(loadExternalScript.args[0][3].nodeName).to.equal('IFRAME'); }); it('should adjust width and height to match slot clientWidth if playersize_auto_adapt is used', function() { - var scriptTag; - sinon.stub(window.document, 'getElementById').returns({ - clientWidth: 200, - appendChild: sinon.stub().callsFake(function(script) { scriptTag = script; }) - }); - var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + let responses = spec.interpretResponse(serverResponse, bidderRequestObj); responses[0].renderer.render(responses[0]); - - expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); - expect(scriptTag.getAttribute('src')).to.equal('https://js.spotx.tv/easi/v1/12345.js'); - expect(scriptTag.getAttribute('data-spotx_content_width')).to.equal('200'); - expect(scriptTag.getAttribute('data-spotx_content_height')).to.equal('150'); - window.document.getElementById.restore(); + expect(loadExternalScript.args[0][4]['data-spotx_content_width']).to.equal('200'); + expect(loadExternalScript.args[0][4]['data-spotx_content_height']).to.equal('150'); }); it('should use a default 4/3 ratio if playersize_auto_adapt is used and response does not contain width or height', function() { delete serverResponse.body.seatbid[0].bid[0].w; delete serverResponse.body.seatbid[0].bid[0].h; - - var scriptTag; - sinon.stub(window.document, 'getElementById').returns({ - clientWidth: 200, - appendChild: sinon.stub().callsFake(function(script) { scriptTag = script; }) - }); - var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + let responses = spec.interpretResponse(serverResponse, bidderRequestObj); responses[0].renderer.render(responses[0]); - - expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); - expect(scriptTag.getAttribute('src')).to.equal('https://js.spotx.tv/easi/v1/12345.js'); - expect(scriptTag.getAttribute('data-spotx_content_width')).to.equal('200'); - expect(scriptTag.getAttribute('data-spotx_content_height')).to.equal('150'); - window.document.getElementById.restore(); + expect(loadExternalScript.args[0][4]['data-spotx_content_width']).to.equal('200'); + expect(loadExternalScript.args[0][4]['data-spotx_content_height']).to.equal('150'); }); }); }); + +function valuesToString(obj) { + let newObj = {}; + for (let prop in obj) { + newObj[prop] = '' + obj[prop]; + } + return newObj; +} From 92a50519d1287d0eb4f567ff44acb6eb7f29682a Mon Sep 17 00:00:00 2001 From: John Ivan Bauzon Date: Wed, 27 Jul 2022 04:48:24 +0800 Subject: [PATCH 077/569] Gumgum Bid Adapter: changes for parameter consistency (#8742) * Gumgum - ADTS-175 Support multiple GG params * ADJS-1165-prebid-adaptor-changes-to-support-jp-products * made tweaks to the skin product for the gumgumBidAdapter * added test for new product id * updated skins parameter for consistency * updated test for updated skins parameter Co-authored-by: Lisa Benmore Co-authored-by: John Bauzon --- modules/gumgumBidAdapter.js | 2 +- modules/gumgumBidAdapter.md | 6 +++--- test/spec/modules/gumgumBidAdapter_spec.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 471b5e41ddc..b77f82030e1 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -370,7 +370,7 @@ function buildRequests(validBidRequests, bidderRequest) { data.pi = 5; } else if (mediaTypes.video) { data.pi = mediaTypes.video.linearity === 2 ? 6 : 7; // invideo : video - } else if (params.product && params.product.toLowerCase() === 'skin') { + } else if (params.product && params.product.toLowerCase() === 'skins') { data.pi = 8; } } else { // legacy params diff --git a/modules/gumgumBidAdapter.md b/modules/gumgumBidAdapter.md index 02b70b8367d..88c9cd29f69 100644 --- a/modules/gumgumBidAdapter.md +++ b/modules/gumgumBidAdapter.md @@ -10,7 +10,7 @@ Maintainer: engineering@gumgum.com GumGum adapter for Prebid.js Please note that both video and in-video products require a mediaType of video. -In-screen, slot, and skin products should have a mediaType of banner. +In-screen, slot, and skins products should have a mediaType of banner. # Test Parameters ``` @@ -51,7 +51,7 @@ var adUnits = [ } ] },{ - code: 'skin-placement', + code: 'skins-placement', sizes: [[300, 50]], mediaTypes: { banner: { @@ -63,7 +63,7 @@ var adUnits = [ bidder: 'gumgum', params: { zone: 'dc9d6be1', // GumGum Zone ID given to the client - product: 'skin', + product: 'skins', bidfloor: 0.03 // CPM bid floor } } diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index c4e9a86eec2..17fff31f132 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -279,8 +279,8 @@ describe('gumgumAdapter', function () { const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.data.pi).to.equal(3); }); - it('should set the correct pi param if product param is found and is equal to skin', function () { - const request = { ...bidRequests[0], params: { ...zoneParam, product: 'Skin' } }; + it('should set the correct pi param if product param is found and is equal to skins', function () { + const request = { ...bidRequests[0], params: { ...zoneParam, product: 'Skins' } }; const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.data.pi).to.equal(8); }); From ddde310c4ab1828351a79ec52b7bfa6ae8254c3b Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 26 Jul 2022 14:34:47 -0700 Subject: [PATCH 078/569] Build system: adjust babel configuration to reduce bundle size (#8732) --- babelConfig.js | 2 +- package-lock.json | 161 ++++++++++++++++++++++++---------------------- package.json | 3 +- 3 files changed, 86 insertions(+), 80 deletions(-) diff --git a/babelConfig.js b/babelConfig.js index 785c6171c42..2592d8dc776 100644 --- a/babelConfig.js +++ b/babelConfig.js @@ -24,7 +24,7 @@ module.exports = function (options = {}) { ], 'plugins': [ [path.resolve(__dirname, './plugins/pbjsGlobals.js'), options], - useLocal('babel-plugin-transform-object-assign'), + [useLocal('@babel/plugin-transform-runtime')], ], } } diff --git a/package-lock.json b/package-lock.json index 19a4ed0770d..6c4269824d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,12 +6,13 @@ "packages": { "": { "name": "prebid.js", - "version": "7.4.0-pre", + "version": "7.8.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", + "@babel/plugin-transform-runtime": "^7.18.9", "@babel/preset-env": "^7.16.8", - "babel-plugin-transform-object-assign": "^6.22.0", + "@babel/runtime": "^7.18.9", "core-js": "^3.13.0", "core-js-pure": "^3.13.0", "criteo-direct-rsa-validate": "^1.1.0", @@ -360,11 +361,11 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -400,9 +401,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", "engines": { "node": ">=6.9.0" } @@ -469,9 +470,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "engines": { "node": ">=6.9.0" } @@ -1335,6 +1336,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz", + "integrity": "sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.1", + "babel-plugin-polyfill-corejs3": "^0.5.2", + "babel-plugin-polyfill-regenerator": "^0.3.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-shorthand-properties": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", @@ -1538,9 +1558,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", - "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -1587,11 +1607,11 @@ } }, "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", + "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" }, "engines": { @@ -4441,14 +4461,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/babel-plugin-transform-object-assign": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz", - "integrity": "sha1-+Z0vZvGgsNSY40bFNZaEdAyqILo=", - "dependencies": { - "babel-runtime": "^6.22.0" - } - }, "node_modules/babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", @@ -4468,7 +4480,7 @@ "version": "2.6.12", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", "dev": true, "hasInstallScript": true }, @@ -4497,6 +4509,7 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, "dependencies": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -4506,7 +4519,8 @@ "version": "2.6.12", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, "hasInstallScript": true }, "node_modules/babel-template": { @@ -6640,6 +6654,7 @@ "version": "3.21.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6671,6 +6686,7 @@ "version": "3.21.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==", + "deprecated": "core-js-pure@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js-pure.", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -19048,7 +19064,8 @@ "node_modules/regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true }, "node_modules/regenerator-transform": { "version": "0.14.5", @@ -20561,6 +20578,7 @@ "version": "9.3.2", "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.3.2.tgz", "integrity": "sha512-u1rfKP4o4ew7Yjbfycv80aNMN2feTiqseAhUhrrx2XtdQGmu7gucpziXe68Z4YfHVqlxVEzo4aUA0Iu3VQOTgQ==", + "deprecated": "standard-version is deprecated. If you're a GitHub user, I recommend https://github.com/googleapis/release-please as an alternative.", "dev": true, "dependencies": { "chalk": "^2.4.2", @@ -21800,20 +21818,6 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, - "node_modules/typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/typescript-compare": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", @@ -23657,11 +23661,11 @@ } }, "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" } }, "@babel/helper-module-transforms": { @@ -23688,9 +23692,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==" + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==" }, "@babel/helper-remap-async-to-generator": { "version": "7.16.8", @@ -23739,9 +23743,9 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==" + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" }, "@babel/helper-validator-option": { "version": "7.16.7", @@ -24281,6 +24285,19 @@ "@babel/helper-plugin-utils": "^7.16.7" } }, + "@babel/plugin-transform-runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz", + "integrity": "sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.1", + "babel-plugin-polyfill-corejs3": "^0.5.2", + "babel-plugin-polyfill-regenerator": "^0.3.1", + "semver": "^6.3.0" + } + }, "@babel/plugin-transform-shorthand-properties": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", @@ -24433,9 +24450,9 @@ } }, "@babel/runtime": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", - "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", "requires": { "regenerator-runtime": "^0.13.4" }, @@ -24475,11 +24492,11 @@ } }, "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", + "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" } }, @@ -26800,14 +26817,6 @@ "@babel/helper-define-polyfill-provider": "^0.3.1" } }, - "babel-plugin-transform-object-assign": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz", - "integrity": "sha1-+Z0vZvGgsNSY40bFNZaEdAyqILo=", - "requires": { - "babel-runtime": "^6.22.0" - } - }, "babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", @@ -26853,6 +26862,7 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -26861,7 +26871,8 @@ "core-js": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "dev": true } } }, @@ -38354,7 +38365,8 @@ "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true }, "regenerator-transform": { "version": "0.14.5", @@ -40581,13 +40593,6 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, - "typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true, - "peer": true - }, "typescript-compare": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", diff --git a/package.json b/package.json index 5a4a3abdb10..9a2e223fe9d 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,8 @@ "dependencies": { "@babel/core": "^7.16.7", "@babel/preset-env": "^7.16.8", - "babel-plugin-transform-object-assign": "^6.22.0", + "@babel/runtime": "^7.18.9", + "@babel/plugin-transform-runtime": "^7.18.9", "core-js": "^3.13.0", "core-js-pure": "^3.13.0", "criteo-direct-rsa-validate": "^1.1.0", From d22f149da61d98c32d751e8687583859ff320aed Mon Sep 17 00:00:00 2001 From: Jason Lydon <95770514+ftxmoJason@users.noreply.github.com> Date: Tue, 26 Jul 2022 18:15:43 -0400 Subject: [PATCH 079/569] FTRACK UserId Submodule: adding more tests for the get ID methods in pbjs (#8737) * FTRACK: adding more tests for the get ID methods in pbjs * FTRACK: making the tests a little DRYer * FTRACK: cleaning up the tests * FTRACK: cleaning up some comments Co-authored-by: Jason Lydon --- modules/userId/eids.js | 6 +- test/spec/modules/eids_spec.js | 30 ++++ test/spec/modules/ftrackIdSystem_spec.js | 192 +++++++++++++---------- 3 files changed, 142 insertions(+), 86 deletions(-) diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 4b5fd55aa21..4c2e3a00107 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -372,11 +372,11 @@ export function createEidsArray(bidRequestUserId) { // ftrack has multiple IDs so we add each one that exists let eid = { 'atype': 1, - 'id': (bidRequestUserId[subModuleKey]['DeviceID'] || []).join('|'), + 'id': (bidRequestUserId.ftrackId.DeviceID || []).join('|'), 'ext': {} } - for (let id in bidRequestUserId[subModuleKey]) { - eid.ext[id] = (bidRequestUserId[subModuleKey][id] || []).join('|'); + for (let id in bidRequestUserId.ftrackId) { + eid.ext[id] = (bidRequestUserId.ftrackId[id] || []).join('|'); } eids.push(eid); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index e0410ff7874..e71058824a4 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -302,6 +302,7 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('uid2', function() { const userId = { uid2: {'id': 'Sample_AD_Token'} @@ -316,6 +317,7 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('kpuid', function() { const userId = { kpuid: 'Sample_Token' @@ -330,6 +332,7 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('tncid', function() { const userId = { tncid: 'TEST_TNCID' @@ -344,6 +347,7 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('pubProvidedId', function() { const userId = { pubProvidedId: [{ @@ -437,6 +441,32 @@ describe('eids array generation for known sub-modules', function() { uids: [{ id: 'some-random-id-value', atype: 1 }] }); }); + + describe('ftrackId', () => { + it('should return the correct EID schema', () => { + expect(createEidsArray({ + ftrackId: { + DeviceID: ['aaa', 'bbb'], + SingleDeviceID: ['ccc', 'ddd'], + HHID: ['eee', 'fff'] + }, + foo: { + bar: 'baz' + }, + lorem: { + ipsum: '' + } + })).to.deep.equal([{ + atype: 1, + id: 'aaa|bbb', + ext: { + DeviceID: 'aaa|bbb', + SingleDeviceID: 'ccc|ddd', + HHID: 'eee|fff' + } + }]); + }); + }); }); describe('Negative case', function () { diff --git a/test/spec/modules/ftrackIdSystem_spec.js b/test/spec/modules/ftrackIdSystem_spec.js index 333a8cce624..2c3f3f8576c 100644 --- a/test/spec/modules/ftrackIdSystem_spec.js +++ b/test/spec/modules/ftrackIdSystem_spec.js @@ -308,107 +308,133 @@ describe('FTRACK ID System', () => { }); }); - describe('Uses ftrack getUserIdsAsEids() method', () => { - it('getUserIdsAsEids using the ftrack submodule and gets three ids (HHID, DeviceId, SingleDeviceId)', () => { + describe('pbjs "get id" methods', () => { + // The full set of ftrack IDs to test against + let expectedIds = { + HHID: ['household_test_id'], + DeviceID: ['device_test_id'], + SingleDeviceID: ['single_device_test_id'] + }; + // The full config mock + let userSyncConfigMock = { + userSync: { + auctionDelay: 10, + userIds: [{ + name: 'ftrack', + value: { + ftrackId: expectedIds + } + }] + } + }; + // The full eids response + let expectedEids = [{ + id: 'device_test_id', + atype: 1, + ext: { + HHID: expectedIds.HHID.join('|'), + DeviceID: expectedIds.DeviceID.join('|'), + SingleDeviceID: expectedIds.SingleDeviceID.join('|') + } + }]; + + beforeEach(() => { init(config); setSubmoduleRegistry([ftrackIdSubmodule]); + }); - const ids = { - ftrackId: { - HHID: ['household_test_id'], - DeviceID: ['device_test_id'], - SingleDeviceID: ['single_device_test_id'] - } + afterEach(() => { + // Reset expectedIds to the default values because some tests overwrite them + expectedIds = { + HHID: ['household_test_id'], + DeviceID: ['device_test_id'], + SingleDeviceID: ['single_device_test_id'] }; - config.setConfig({ - userSync: { - auctionDelay: 10, - userIds: [{ - name: 'ftrack', value: ids, - }] - } - }); - - getGlobal().getUserIdsAsync().then((ids) => { - expect(getGlobal().getUserIdsAsEids()).to.deep.equal([{ - id: 'device_test_id', - atype: 1, - ext: { - HHID: 'household_test_id', - DeviceID: 'device_test_id', - SingleDeviceID: 'single_device_test_id', - } - }]); - }); }); - it('gets only the deviceId', () => { - init(config); - setSubmoduleRegistry([ftrackIdSubmodule]); + describe('pbjs.getUserIdsAsync()', () => { + it('should return the IDs in the correct schema', () => { + config.setConfig(userSyncConfigMock); - const ids = { ftrackId: { DeviceID: ['device_test_id'] } }; - config.setConfig({ - userSync: { - auctionDelay: 10, - userIds: [{ - name: 'ftrack', value: ids, - }] - } + getGlobal().getUserIdsAsync().then(ids => { + expect(ids).to.deep.equal({ + ftrackId: expectedIds + }); + }); }); + }); + + describe('pbjs.getUserIds()', () => { + it('should return the IDs in the correct schema', () => { + config.setConfig(userSyncConfigMock); - getGlobal().getUserIdsAsync().then((ids) => { - expect(getGlobal().getUserIdsAsEids()).to.deep.equal([{ - id: 'device_test_id', - atype: 1, - ext: { DeviceID: 'device_test_id' } - }]); + expect(getGlobal().getUserIds()).to.deep.equal({ + 'ftrackId': expectedIds + }); }); }); - it('gets only the user household id', () => { - init(config); - setSubmoduleRegistry([ftrackIdSubmodule]); + describe('pbjs.getUserIdsAsEids()', () => { + it('should return the correct EIDs schema', () => { + let userSyncConfig = Object.assign({}, userSyncConfigMock); - const ids = { ftrackId: { HHID: ['household_test_id'], } }; - config.setConfig({ - userSync: { - auctionDelay: 10, - userIds: [{ - name: 'ftrack', value: ids, - }] - } - }); + config.setConfig(userSyncConfig); - getGlobal().getUserIdsAsync().then((ids) => { - expect(getGlobal().getUserIdsAsEids()).to.deep.equal([{ - id: '', - atype: 1, - ext: { HHID: 'household_test_id' } - }]); + expect(getGlobal().getUserIdsAsEids()).to.deep.equal(expectedEids); }); - }); - it('gets only the deviceId', () => { - init(config); - setSubmoduleRegistry([ftrackIdSubmodule]); + describe('by ID type:', () => { + it('- DeviceID', () => { + let userSyncConfig = Object.assign({}, userSyncConfigMock); - const ids = { ftrackId: { SingleDeviceID: ['single_device_test_id'] } }; - config.setConfig({ - userSync: { - auctionDelay: 10, - userIds: [{ - name: 'ftrack', value: ids, - }] - } - }); + userSyncConfig.userSync.userIds[0].value.ftrackId = { + DeviceID: ['device_test_id'] + }; + + let expectedEidsClone = expectedEids.slice(); + expectedEidsClone[0].ext = { + DeviceID: expectedIds.DeviceID.join('|') + }; + + config.setConfig(userSyncConfig); + + expect(getGlobal().getUserIdsAsEids()).to.deep.equal(expectedEidsClone); + }); + + it('- HHID', () => { + let userSyncConfig = Object.assign({}, userSyncConfigMock); + userSyncConfig.userSync.userIds[0].value.ftrackId = { + HHID: ['household_test_id'] + }; - getGlobal().getUserIdsAsync().then((ids) => { - expect(getGlobal().getUserIdsAsEids()).to.deep.equal([{ - id: '', - atype: 1, - ext: { SingleDeviceID: 'single_device_test_id' } - }]); + let expectedEidsClone = expectedEids.slice(); + expectedEidsClone[0].id = ''; + expectedEidsClone[0].ext = { + HHID: expectedIds.HHID.join('|') + }; + + config.setConfig(userSyncConfig); + + expect(getGlobal().getUserIdsAsEids()).to.deep.equal(expectedEidsClone); + }); + + it('- SingleDeviceID', () => { + let userSyncConfig = Object.assign({}, userSyncConfigMock); + userSyncConfig.userSync.userIds[0].value.ftrackId = { + SingleDeviceID: ['single_device_test_id'] + }; + + let expectedEidsClone = expectedEids.slice(); + expectedEidsClone[0].id = ''; + expectedEidsClone[0].ext = { + SingleDeviceID: expectedIds.SingleDeviceID.join('|') + }; + + config.setConfig(userSyncConfig); + + expect(getGlobal().getUserIdsAsEids()).to.deep.equal(expectedEidsClone); + }); }); }); - }); + }) }); From fbcecc9ee7d1f86411c90109365ee6938df50bad Mon Sep 17 00:00:00 2001 From: Regulyarniy Nikolay Date: Wed, 27 Jul 2022 23:58:29 +1000 Subject: [PATCH 080/569] Videonow Bid Adapter: Initial Bid Adapter Release (#8669) * Videonow bid adapter: add videonow bid adapter * Update videonowBidAdapter.md Co-authored-by: Patrick McCann --- modules/videonowBidAdapter.js | 122 +++++++++++++++++++ modules/videonowBidAdapter.md | 37 ++++++ test/spec/modules/videonowBidAdapter_spec.js | 80 ++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 modules/videonowBidAdapter.js create mode 100644 modules/videonowBidAdapter.md create mode 100644 test/spec/modules/videonowBidAdapter_spec.js diff --git a/modules/videonowBidAdapter.js b/modules/videonowBidAdapter.js new file mode 100644 index 00000000000..bfbc07fdff1 --- /dev/null +++ b/modules/videonowBidAdapter.js @@ -0,0 +1,122 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import {_each, getBidIdParameter, getValue, logError, logInfo} from '../src/utils.js'; + +const BIDDER_CODE = 'videonow'; +const RTB_URL = 'https://adx.videonow.ru/yhb' +const DEFAULT_CURRENCY = 'RUB' +const DEFAULT_CODE_TYPE = 'combo' +const TTL_SECONDS = 60 * 5 + +export const spec = { + + code: BIDDER_CODE, + url: RTB_URL, + supportedMediaTypes: [ BANNER ], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bidRequest The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bidRequest) { + if (!bidRequest.params) { + return false; + } + + if (!bidRequest.params.pId) { + logError('failed validation: pId not declared'); + return false + } + + return true + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} validBidRequests - an array of bids + * @param bidderRequest + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + logInfo('validBidRequests', validBidRequests); + + const bidRequests = []; + + _each(validBidRequests, (bid) => { + const bidId = getBidIdParameter('bidId', bid) + const placementId = getValue(bid.params, 'pId') + const currency = getValue(bid.params, 'currency') || DEFAULT_CURRENCY + const url = getValue(bid.params, 'url') || RTB_URL + const codeType = getValue(bid.params, 'codeType') || DEFAULT_CODE_TYPE + const sizes = getValue(bid, 'sizes') + + bidRequests.push({ + method: 'POST', + url, + data: { + places: [ + { + id: bidId, + placementId, + codeType, + sizes + } + ], + settings: { + currency, + } + }, + }) + }) + + return bidRequests; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param {BidRequest} bidRequest The bid params + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + logInfo('serverResponse', serverResponse.body); + + const responsesBody = serverResponse ? serverResponse.body : {}; + const bidResponses = []; + try { + if (!responsesBody?.bids?.length) { + return []; + } + + _each(responsesBody.bids, (bid) => { + if (bid?.displayCode) { + const bidResponse = { + requestId: bid.id, + cpm: bid.cpm, + currency: bid.currency, + width: bid.size.width, + height: bid.size.height, + ad: bid.displayCode, + ttl: TTL_SECONDS, + creativeId: bid.id, + netRevenue: true, + meta: { + advertiserDomains: bid.adDomain ? [bid.adDomain] : [] + } + }; + bidResponses.push(bidResponse) + } + }) + } catch (error) { + logError(error); + } + + return bidResponses + }, +} + +registerBidder(spec); diff --git a/modules/videonowBidAdapter.md b/modules/videonowBidAdapter.md new file mode 100644 index 00000000000..0762880f666 --- /dev/null +++ b/modules/videonowBidAdapter.md @@ -0,0 +1,37 @@ +# Overview + +**Module Name**: Videonow Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: nregularniy@videonow.ru + +# Description + +Videonow Bidder Adapter for Prebid.js. About: https://videonow.ru/ + + +Use `videonow` as bidder: + +# Params +- `pId` required, profile ID +- `currency` optional, currency, default is 'RUB' +- `url` optional, for debug, bidder url +- `codeType` optional, for debug, yhb codeType + +## AdUnits configuration example +``` + var adUnits = [{ + code: 'your-slot', //use exactly the same code as your slot div id. + mediaTypes: { + banner: { + sizes: [[640, 480]] + } + }, + bids: [{ + bidder: 'videonow', + params: { + pId: '1234', + currency: 'RUB', + } + }] + }]; +``` diff --git a/test/spec/modules/videonowBidAdapter_spec.js b/test/spec/modules/videonowBidAdapter_spec.js new file mode 100644 index 00000000000..c9eb5ba0bbf --- /dev/null +++ b/test/spec/modules/videonowBidAdapter_spec.js @@ -0,0 +1,80 @@ +import {expect} from 'chai'; +import {spec} from 'modules/videonowBidAdapter'; + +describe('videonowBidAdapter', function () { + it('minimal params', function () { + expect(spec.isBidRequestValid({ + bidder: 'videonow', + params: { + pId: 'advDesktopBillboard' + }})).to.equal(true) + }) + + it('minimal params no placementId', function () { + expect(spec.isBidRequestValid({ + bidder: 'videonow', + params: { + currency: `GBP` + }})).to.equal(false) + }) + + it('generated_params common case', function () { + const bidRequestData = [{ + bidId: 'bid1234', + bidder: 'videonow', + params: { + pId: 'advDesktopBillboard', + currency: `GBP` + }, + sizes: [[240, 400]] + }]; + + const request = spec.buildRequests(bidRequestData); + const req_data = request[0].data; + + expect(req_data.places[0].id).to.equal(`bid1234`) + expect(req_data.places[0].placementId).to.equal(`advDesktopBillboard`) + expect(req_data.settings.currency).to.equal(`GBP`) + expect(req_data.places[0].sizes[0][0]).to.equal(240); + expect(req_data.places[0].sizes[0][1]).to.equal(400); + }); + + it('response_params common case', function () { + const bidRequestData = { + data: { + bidId: 'bid1234' + } + }; + + const serverResponse = { + body: { + bids: [ + { + 'displayCode': 'test html', + 'id': '123456', + 'cpm': 375, + 'currency': 'RUB', + 'placementId': 'profileName', + 'codeType': 'js', + 'size': { + 'width': 640, + 'height': 480 + } + } + ] + } + }; + + const bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.requestId).to.equal('123456') + expect(bid.cpm).to.equal(375); + expect(bid.currency).to.equal('RUB'); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(480); + expect(bid.ad).to.equal('test html'); + expect(bid.creativeId).to.equal(`123456`) + expect(bid.netRevenue).to.equal(true); + }); +}) From 15b688c9a4f15a7669fd1fa579f9c76e1a917949 Mon Sep 17 00:00:00 2001 From: rcheptanariu <35690143+rcheptanariu@users.noreply.github.com> Date: Wed, 27 Jul 2022 18:54:16 +0300 Subject: [PATCH 081/569] InvibesBidAdapter - local storage bypass fixed (#8720) * InvibesBidAdapter - local storage bypass fixed * InvibesBidAdapter - fixed error when param not defined Co-authored-by: Patrick McCann --- modules/invibesBidAdapter.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 99434e18adb..2f80b89fb9c 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -381,7 +381,10 @@ function getUserIds(bidUserId) { function parseQueryStringParams() { let params = {}; try { - params = JSON.parse(readFromLocalStorage('ivbs')); + let storedParam = storage.getDataFromLocalStorage('ivbs'); + if (storedParam != null) { + params = JSON.parse(storedParam); + } } catch (e) { } let re = /[\\?&]([^=]+)=([^\\?&#]+)/g; From 7c267f73935eed73940c3b83a3b390ac42f04aca Mon Sep 17 00:00:00 2001 From: jsfledd Date: Wed, 27 Jul 2022 11:08:24 -0700 Subject: [PATCH 082/569] Nativo Bid Adapter: add Price Floors Module support (#8666) * Initial nativoBidAdapter document creation (js, md and spec) * Fulling working prebid using nativoBidAdapter. Support for GDPR and CCPA in user syncs. * Added defult size settings based on the largest ad unit. Added response body validation. Added consent to request url qs params. * Changed bidder endpoint url * Changed double quotes to single quotes. * Reverted package-json.lock to remove modifications from PR * Added optional bidder param 'url' so the ad server can force- match an existing placement * Lint fix. Added space after if. * Added new QS param to send various adUnit data to adapter endpopint * Updated unit test for new QS param * Added qs param to keep track of ad unit refreshes * Updated bidMap key default value * Updated refresh increment logic * Refactored spread operator for IE11 support * Updated isBidRequestValid check * Refactored Object.enties to use Object.keys to fix CircleCI testing errors * Updated bid mapping key creation to prioritize ad unit code over placementId * Added filtering by ad, advertiser and campaign. * Merged master * Added more robust bidDataMap with multiple key access * Deduped filer values * Rolled back package.json * Duped upstream/master's package.lock file ... not sure how it got changed in the first place * Small refactor of filterData length check. Removed comparison with 0 since a length value of 0 is already falsy. * Added bid sizes to request * Fixed function name in spec. Added unit tests. * Added priceFloor module support * Added protection agains empty url parameter --- modules/nativoBidAdapter.js | 198 ++++++++++++++++++--- test/spec/modules/nativoBidAdapter_spec.js | 149 +++++++++++++++- 2 files changed, 319 insertions(+), 28 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 271ecac7aa2..54c99b17834 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -11,6 +11,8 @@ const GVLID = 263 const TIME_TO_LIVE = 360 const SUPPORTED_AD_TYPES = [BANNER] +const FLOOR_PRICE_CURRENCY = 'USD' +const PRICE_FLOOR_WILDCARD = '*' /** * Keep track of bid data by keys @@ -131,84 +133,106 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { + // Parse values from bid requests const placementIds = new Set() const bidDataMap = BidDataMap() const placementSizes = { length: 0 } + const floorPriceData = {} let placementId, pageUrl - validBidRequests.forEach((request) => { + validBidRequests.forEach((bidRequest) => { pageUrl = deepAccess( - request, + bidRequest, 'params.url', - bidderRequest.refererInfo.page ) - placementId = deepAccess(request, 'params.placementId') + if (pageUrl == undefined || pageUrl === '') { + pageUrl = bidderRequest.refererInfo.page + } + + placementId = deepAccess(bidRequest, 'params.placementId') - const bidDataKeys = [request.adUnitCode] + const bidDataKeys = [bidRequest.adUnitCode] if (placementId && !placementIds.has(placementId)) { placementIds.add(placementId) bidDataKeys.push(placementId) - placementSizes[placementId] = request.sizes + placementSizes[placementId] = bidRequest.sizes placementSizes.length++ } const bidData = { - bidId: request.bidId, - size: getLargestSize(request.sizes), + bidId: bidRequest.bidId, + size: getLargestSize(bidRequest.sizes), } bidDataMap.addBidData(bidData, bidDataKeys) + + const bidRequestFloorPriceData = parseFloorPriceData(bidRequest) + if (bidRequestFloorPriceData) { + floorPriceData[bidRequest.adUnitCode] = bidRequestFloorPriceData + } }) bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap // Build adUnit data - const adUnitData = { - adUnits: validBidRequests.map((adUnit) => { - // Track if we've already requested for this ad unit code - adUnitsRequested[adUnit.adUnitCode] = - adUnitsRequested[adUnit.adUnitCode] !== undefined - ? adUnitsRequested[adUnit.adUnitCode] + 1 - : 0 - return { - adUnitCode: adUnit.adUnitCode, - mediaTypes: adUnit.mediaTypes, - } - }), - } + const adUnitData = buildAdUnitData(validBidRequests) - // Build QS Params + // Build basic required QS Params let params = [ + // Prebid request id { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, + // Ad unit data { key: 'ntv_ppc', value: btoa(JSON.stringify(adUnitData)), // Convert to Base 64 }, + // Number count of requests per ad unit { key: 'ntv_dbr', - value: btoa(JSON.stringify(adUnitsRequested)), + value: btoa(JSON.stringify(adUnitsRequested)), // Convert to Base 64 }, + // Page url { key: 'ntv_url', value: encodeURIComponent(pageUrl), }, ] + // Floor pricing + if (Object.keys(floorPriceData).length) { + params.unshift({ + key: 'ntv_ppf', + value: btoa(JSON.stringify(floorPriceData)), + }) + } + // Add filtering if (adsToFilter.size > 0) { - params.unshift({ key: 'ntv_atf', value: Array.from(adsToFilter).join(',') }) + params.unshift({ + key: 'ntv_atf', + value: Array.from(adsToFilter).join(','), + }) } if (advertisersToFilter.size > 0) { - params.unshift({ key: 'ntv_avtf', value: Array.from(advertisersToFilter).join(',') }) + params.unshift({ + key: 'ntv_avtf', + value: Array.from(advertisersToFilter).join(','), + }) } if (campaignsToFilter.size > 0) { - params.unshift({ key: 'ntv_ctf', value: Array.from(campaignsToFilter).join(',') }) + params.unshift({ + key: 'ntv_ctf', + value: Array.from(campaignsToFilter).join(','), + }) } // Placement Sizes if (placementSizes.length) { - params.unshift({ key: 'ntv_pas', value: btoa(JSON.stringify(placementSizes)) }) + params.unshift({ + key: 'ntv_pas', + value: btoa(JSON.stringify(placementSizes)), + }) } // Add placement IDs @@ -429,6 +453,126 @@ export const spec = { registerBidder(spec) // Utils +export function parseFloorPriceData(bidRequest) { + if (typeof bidRequest.getFloor !== 'function') return + + // Setup price floor data per bid request + let bidRequestFloorPriceData = {} + let bidMediaTypes = bidRequest.mediaTypes + let sizeOptions = new Set() + // Step through meach media type so we can get floor data for each media type per bid request + Object.keys(bidMediaTypes).forEach((mediaType) => { + // Setup price floor data per media type + let mediaTypeData = bidMediaTypes[mediaType] + let mediaTypeFloorPriceData = {} + // Step through each size of the media type so we can get floor data for each size per media type + mediaTypeData.sizes.forEach((size) => { + // Get floor price data per the getFloor method and respective media type / size combination + const priceFloorData = bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType, + size, + }) + // Save the data and track the sizes + mediaTypeFloorPriceData[sizeToString(size)] = priceFloorData.floor + sizeOptions.add(size) + }) + bidRequestFloorPriceData[mediaType] = mediaTypeFloorPriceData + + // Get floor price of current media type with a wildcard size + const sizeWildcardFloor = getSizeWildcardPrice(bidRequest, mediaType) + // Save the wildcard floor price if it was retrieved successfully + if (sizeWildcardFloor.floor > 0) { + mediaTypeFloorPriceData['*'] = sizeWildcardFloor.floor + } + }) + + // Get floor price for wildcard media type using all of the sizes present in the previous media types + const mediaWildCardPrices = getMediaWildcardPrices(bidRequest, [ + PRICE_FLOOR_WILDCARD, + ...Array.from(sizeOptions), + ]) + bidRequestFloorPriceData['*'] = mediaWildCardPrices + + return bidRequestFloorPriceData +} + +/** + * Get price floor data by always setting the size value to the wildcard for a specific size + * @param {Object} bidRequest - The bid request + * @param {String} mediaType - The media type + * @returns {Object} - Bid floor data + */ +export function getSizeWildcardPrice(bidRequest, mediaType) { + return bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType, + size: PRICE_FLOOR_WILDCARD, + }) +} + +/** + * Get price data for a range of sizes and always setting the media type to the wildcard value + * @param {*} bidRequest - The bid request + * @param {*} sizes - The sizes to get the floor price data for + * @returns {Object} - Bid floor data + */ +export function getMediaWildcardPrices( + bidRequest, + sizes = [PRICE_FLOOR_WILDCARD] +) { + const sizePrices = {} + sizes.forEach((size) => { + // MODIFY the bid request's mediaTypes property (so we can get the wildcard media type value) + const temp = bidRequest.mediaTypes + bidRequest.mediaTypes = { PRICE_FLOOR_WILDCARD: temp.sizes } + // Get price floor data + const priceFloorData = bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType: PRICE_FLOOR_WILDCARD, + size, + }) + // RESTORE initial property value + bidRequest.mediaTypes = temp + + // Only save valid floor price data + const key = + size !== PRICE_FLOOR_WILDCARD ? sizeToString(size) : PRICE_FLOOR_WILDCARD + sizePrices[key] = priceFloorData.floor + }) + return sizePrices +} + +/** + * Format size array to a string + * @param {Array} size - Size data [width, height] + * @returns {String} - Formated size string + */ +export function sizeToString(size) { + if (!Array.isArray(size) || size.length < 2) return '' + return `${size[0]}x${size[1]}` +} + +/** + * Build the ad unit data to send back to the request endpoint + * @param {Array} requests - Bid requests + * @returns {Array} - Array of ad unit data + */ +function buildAdUnitData(requests) { + return requests.map((request) => { + // Track if we've already requested for this ad unit code + adUnitsRequested[request.adUnitCode] = + adUnitsRequested[request.adUnitCode] !== undefined + ? adUnitsRequested[request.adUnitCode] + 1 + : 0 + // Return a new object with only the data we need + return { + adUnitCode: request.adUnitCode, + mediaTypes: request.mediaTypes, + } + }) +} + /** * Append QS param to existing string * @param {String} str - String to append to diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 0690c7f90e1..be6b07f9acc 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -1,5 +1,11 @@ import { expect } from 'chai' import { spec, BidDataMap } from 'modules/nativoBidAdapter.js' +import { + getSizeWildcardPrice, + getMediaWildcardPrices, + sizeToString, + parseFloorPriceData, +} from '../../../modules/nativoBidAdapter' describe('bidDataMap', function () { it('Should fail gracefully if no key value pairs have been added and no key is sent', function () { @@ -94,7 +100,7 @@ describe('nativoBidAdapterTests', function () { const bidRequestString = JSON.stringify(bidRequest) let bidRequests - beforeEach(function() { + beforeEach(function () { // Clone bidRequest each time bidRequests = [JSON.parse(bidRequestString)] }) @@ -119,6 +125,20 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.include('ntv_pas') }) + it('ntv_url parameter should NOT be empty even if the utl parameter was set as an empty value', function () { + bidRequests[0].params.url = '' + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + + expect(request.url).to.exist + expect(request.url).to.be.a('string') + expect(request.url).to.not.be.empty + }) + it('url should NOT contain placement specific query string parameters if placementId option is not provided', function () { bidRequests[0].params = {} const request = spec.buildRequests(bidRequests, { @@ -489,3 +509,130 @@ describe('Response to Request Filter Flow', () => { expect(request.url).to.include('ntv_ctf=234') }) }) + +describe('sizeToString', () => { + it('Formats size array correctly', () => { + const sizeString = sizeToString([300, 250]) + expect(sizeString).to.be.equal('300x250') + }) + + it('Returns an empty array for invalid data', () => { + // Not an array + let sizeString = sizeToString(300, 350) + expect(sizeString).to.be.equal('') + // Single entry + sizeString = sizeToString([300]) + expect(sizeString).to.be.equal('') + // Undefined + sizeString = sizeToString(undefined) + expect(sizeString).to.be.equal('') + }) +}) + +describe('getSizeWildcardPrice', () => { + it('Generates the correct floor price data', () => { + let floorPrice = { + currency: 'USD', + floor: 1.0, + } + let getFloorMock = () => { + return floorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [300, 250], + }, + }, + } + + let result = getSizeWildcardPrice(bidRequest, 'banner') + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: 'banner', + size: '*', + }) + ).to.be.true + expect(result).to.equal(floorPrice) + }) +}) + +describe('getMediaWildcardPrices', () => { + it('Generates the correct floor price data', () => { + let defaultFloorPrice = { + currency: 'USD', + floor: 1.1, + } + let sizefloorPrice = { + currency: 'USD', + floor: 2.2, + } + let getFloorMock = ({ currency, mediaType, size }) => { + if (Array.isArray(size)) return sizefloorPrice + + return defaultFloorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [300, 250], + }, + }, + } + + let result = getMediaWildcardPrices(bidRequest, ['*', [300, 250]]) + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: '*', + size: '*', + }) + ).to.be.true + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: '*', + size: [300, 250], + }) + ).to.be.true + expect(result).to.deep.equal({ '*': 1.1, '300x250': 2.2 }) + }) +}) + +describe('parseFloorPriceData', () => { + it('Generates the correct floor price data', () => { + let defaultFloorPrice = { + currency: 'USD', + floor: 1.1, + } + let sizefloorPrice = { + currency: 'USD', + floor: 2.2, + } + let getFloorMock = ({ currency, mediaType, size }) => { + if (Array.isArray(size)) return sizefloorPrice + + return defaultFloorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + } + + let result = parseFloorPriceData(bidRequest) + expect(result).to.deep.equal({ + '*': { '*': 1.1, '300x250': 2.2 }, + banner: { '*': 1.1, '300x250': 2.2 }, + }) + }) +}) From 7e12865005b876fd46c93ce399584d7255be0b23 Mon Sep 17 00:00:00 2001 From: ahmadlob <109217988+ahmadlob@users.noreply.github.com> Date: Thu, 28 Jul 2022 05:33:48 +0300 Subject: [PATCH 083/569] Taboola Bid Adapter: dynamic ttl (#8747) * update-ttl-passing * Update taboolaBidAdapter_spec.js * add fallback default value in case of null * add semicolons * . * . * . * .. * ... --- modules/taboolaBidAdapter.js | 5 ++-- test/spec/modules/taboolaBidAdapter_spec.js | 26 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 1a35f9e548e..e94e7aba7ca 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -222,9 +222,8 @@ function getBid(requestId, currency, bidResponse) { if (!bidResponse) { return; } - const { - price: cpm, crid: creativeId, adm: ad, w: width, h: height, adomain: advertiserDomains, meta = {} + price: cpm, crid: creativeId, adm: ad, w: width, h: height, exp: ttl, adomain: advertiserDomains, meta = {} } = bidResponse; if (advertiserDomains && advertiserDomains.length > 0) { @@ -233,7 +232,7 @@ function getBid(requestId, currency, bidResponse) { return { requestId, - ttl: 60, + ttl, mediaType: BANNER, cpm, creativeId, diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 4749a6a5919..739335e8f1f 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -311,6 +311,7 @@ describe('Taboola Adapter', function () { 'crid': '278195503434041083381', 'w': 300, 'h': 250, + 'exp': 60, 'lurl': 'http://us-trc.taboola.com/sample' } ], @@ -386,6 +387,31 @@ describe('Taboola Adapter', function () { const res = spec.interpretResponse(serverResponse, request) expect(res).to.deep.equal(expectedRes) }); + + it('should set the correct ttl form the response', function () { + // set exp-ttl to be 125 + const [bid] = serverResponse.body.seatbid[0].bid; + serverResponse.body.seatbid[0].bid[0].exp = 125; + const expectedRes = [ + { + requestId: request.bids[0].bidId, + cpm: bid.price, + creativeId: bid.crid, + ttl: 125, + netRevenue: false, + currency: serverResponse.body.cur, + mediaType: 'banner', + ad: bid.adm, + width: bid.w, + height: bid.h, + meta: { + 'advertiserDomains': bid.adomain + }, + } + ]; + const res = spec.interpretResponse(serverResponse, request); + expect(res).to.deep.equal(expectedRes); + }); }) describe('userData', function () { From 963c1fbe9c87419c2e3e2757c4405878dd51e823 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Jul 2022 08:24:50 -0400 Subject: [PATCH 084/569] Bump parse-url from 6.0.0 to 6.0.5 (#8756) Bumps [parse-url](https://github.com/IonicaBizau/parse-url) from 6.0.0 to 6.0.5. - [Release notes](https://github.com/IonicaBizau/parse-url/releases) - [Commits](https://github.com/IonicaBizau/parse-url/compare/6.0.0...6.0.5) --- updated-dependencies: - dependency-name: parse-url dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c4269824d7..ccf4d92a711 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18110,9 +18110,9 @@ } }, "node_modules/parse-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", - "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.5.tgz", + "integrity": "sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA==", "dev": true, "dependencies": { "is-ssh": "^1.3.0", @@ -37633,9 +37633,9 @@ } }, "parse-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", - "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.5.tgz", + "integrity": "sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA==", "dev": true, "requires": { "is-ssh": "^1.3.0", From c94c8de82d2aea4076e1067e4ab582fa3ebc22f6 Mon Sep 17 00:00:00 2001 From: Carlos Felix Date: Thu, 28 Jul 2022 07:46:03 -0500 Subject: [PATCH 085/569] 33Across Bid Adapter: Obtain display-related attributes (#8730) * capture display-related client side attributes * rename the client display attributes * Obtain the UA entropy values * apply CR feedback, reuse win constant * pass gpid into build * feedback changes * fix missing native property in some browsers * rename entropy fields * only store entropy data when it's present * remove client hints Co-authored-by: Anthony Lin Co-authored-by: anthonyjl92 --- modules/33acrossBidAdapter.js | 74 +++++- test/spec/modules/33acrossBidAdapter_spec.js | 231 ++++++++++++++++++- 2 files changed, 301 insertions(+), 4 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 700b1409da2..ad2b092baa8 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -254,6 +254,7 @@ function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageU }); ttxRequest.site = { id: siteId }; + ttxRequest.device = _buildDeviceORTB(); if (pageUrl) { ttxRequest.site.page = pageUrl; @@ -333,12 +334,15 @@ function setExtensions(obj = {}, extFields) { // BUILD REQUESTS: IMP function _buildImpORTB(bidRequest) { + const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); + const imp = { id: bidRequest.bidId, ext: { ttx: { prod: deepAccess(bidRequest, 'params.productId') - } + }, + ...(gpid ? { gpid } : {}) } }; @@ -734,6 +738,74 @@ function _createSync({ siteId = 'zzz000000000003zzz', gdprConsent = {}, uspConse return sync; } +// BUILD REQUESTS: DEVICE +function _buildDeviceORTB() { + const win = getWindowSelf(); + + return { + ext: { + ttx: { + ...getScreenDimensions(), + pxr: win.devicePixelRatio, + vp: getViewportDimensions(), + ah: win.screen.availHeight, + mtp: win.navigator.maxTouchPoints + } + } + }; +} + +function getTopMostAccessibleWindow() { + let mostAccessibleWindow = getWindowSelf(); + + try { + while (mostAccessibleWindow.parent !== mostAccessibleWindow && + mostAccessibleWindow.parent.document) { + mostAccessibleWindow = mostAccessibleWindow.parent; + } + } catch (err) { + // Do not throw an exception if we can't access the topmost frame. + } + + return mostAccessibleWindow; +} + +function getViewportDimensions() { + const topWin = getTopMostAccessibleWindow(); + const documentElement = topWin.document.documentElement; + + return { + w: documentElement.clientWidth, + h: documentElement.clientHeight, + }; +} + +function getScreenDimensions() { + const { + innerWidth: windowWidth, + innerHeight: windowHeight, + screen + } = getWindowSelf(); + + const [biggerDimension, smallerDimension] = [ + Math.max(screen.width, screen.height), + Math.min(screen.width, screen.height), + ]; + + if (windowHeight > windowWidth) { // Portrait mode + return { + w: smallerDimension, + h: biggerDimension, + }; + } + + // Landscape mode + return { + w: biggerDimension, + h: smallerDimension, + }; +} + export const spec = { NON_MEASURABLE, diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index 4c5ff808bc0..3657f7da912 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -30,6 +30,21 @@ describe('33acrossBidAdapter:', function () { site: { id: siteId }, + device: { + ext: { + ttx: { + w: 1024, + h: 728, + pxr: 2, + vp: { + w: 800, + h: 600 + }, + ah: 500, + mtp: 0 + } + } + }, id: 'r1', regs: { ext: { @@ -117,7 +132,7 @@ describe('33acrossBidAdapter:', function () { this.withProduct = (prod = 'siab') => { ttxRequest.imp.forEach((imp) => { - Object.assign(imp, { + utils.mergeDeep(imp, { ext: { ttx: { prod @@ -129,6 +144,18 @@ describe('33acrossBidAdapter:', function () { return this; }; + this.withGpid = (gpid) => { + ttxRequest.imp.forEach((imp) => { + utils.mergeDeep(imp, { + ext: { + gpid + } + }); + }); + + return this; + }; + this.withGdprConsent = (consent, gdpr) => { Object.assign(ttxRequest, { user: { @@ -166,6 +193,12 @@ describe('33acrossBidAdapter:', function () { return this; }; + this.withDevice = (device) => { + utils.mergeDeep(ttxRequest, { device }); + + return this; + }; + this.withPageUrl = pageUrl => { Object.assign(ttxRequest.site, { page: pageUrl @@ -360,8 +393,21 @@ describe('33acrossBidAdapter:', function () { }; win = { parent: null, + devicePixelRatio: 2, + screen: { + width: 1024, + height: 728, + availHeight: 500 + }, + navigator: { + maxTouchPoints: 0 + }, document: { - visibilityState: 'visible' + visibilityState: 'visible', + documentElement: { + clientWidth: 800, + clientHeight: 600 + } }, innerWidth: 800, @@ -373,7 +419,6 @@ describe('33acrossBidAdapter:', function () { .withBanner() .build() ); - sandbox = sinon.sandbox.create(); sandbox.stub(Date, 'now').returns(1); sandbox.stub(document, 'getElementById').returns(element); @@ -755,6 +800,151 @@ describe('33acrossBidAdapter:', function () { const [ buildRequest ] = spec.buildRequests(bidRequests); validateBuiltServerRequest(buildRequest, serverRequest); }); + + context('when all the wrapping windows are accessible', function() { + it('returns the viewport dimensions of the top most accessible window', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withDevice({ + ext: { + ttx: { + vp: { + w: 6789, + h: 2345 + } + } + } + }) + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + sandbox.stub(win, 'parent').value({ + document: { + documentElement: { + clientWidth: 1234, + clientHeight: 4567 + } + }, + parent: { + document: { + documentElement: { + clientWidth: 6789, + clientHeight: 2345 + } + }, + } + }); + + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); + }); + }); + + context('when one of the wrapping windows cannot be accessed', function() { + it('returns the viewport dimensions of the top most accessible window', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withDevice({ + ext: { + ttx: { + vp: { + w: 9876, + h: 5432 + } + } + } + }) + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const notAccessibleParentWindow = {}; + + Object.defineProperty(notAccessibleParentWindow, 'document', { + get() { throw new Error('fakeError'); } + }); + + sandbox.stub(win, 'parent').value({ + document: { + documentElement: { + clientWidth: 1234, + clientHeight: 4567 + } + }, + parent: { + parent: notAccessibleParentWindow, + document: { + documentElement: { + clientWidth: 9876, + clientHeight: 5432 + } + }, + } + }); + + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); + }); + }); + }); + + it('returns the screen dimensions', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withDevice({ + ext: { + ttx: { + w: 1024, + h: 728 + } + } + }) + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + win.screen.width = 1024; + win.screen.height = 728; + + const [ buildRequest ] = spec.buildRequests(bidRequests); + + validateBuiltServerRequest(buildRequest, serverRequest); + }); + + context('when the window height is greater than the width', function() { + it('returns the smaller screen dimension as the width', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withDevice({ + ext: { + ttx: { + w: 728, + h: 1024 + } + } + }) + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + win.screen.width = 1024; + win.screen.height = 728; + + win.innerHeight = 728; + win.innerWidth = 727; + + const [ buildRequest ] = spec.buildRequests(bidRequests); + + validateBuiltServerRequest(buildRequest, serverRequest); + }); }); context('when tab is inactive', function() { @@ -977,6 +1167,41 @@ describe('33acrossBidAdapter:', function () { }); }); + context('when Global Placement ID (gpid) is defined', function() { + let bidderRequest; + + beforeEach(function() { + bidderRequest = {}; + }); + + it('passes the Global Placement ID (gpid) in the request', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() + .withGpid('fakeGPID0') + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + let copyBidRequest = utils.deepClone(bidRequests); + const bidRequestsWithGpid = copyBidRequest.map(function(bidRequest, index) { + return { + ...bidRequest, + ortb2Imp: { + ext: { + gpid: 'fakeGPID' + index + } + } + }; + }); + + const [ builtServerRequest ] = spec.buildRequests(bidRequestsWithGpid, bidderRequest); + + validateBuiltServerRequest(builtServerRequest, serverRequest); + }); + }); + context('when referer value is not available', function() { it('returns corresponding server requests without site.page set', function() { const bidderRequest = { From 58123576ddd62c4de65f8f0e10f6b65afb96218e Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 28 Jul 2022 05:52:16 -0700 Subject: [PATCH 086/569] Core & multiple modules: strict purpose 1 consent option; do not require vendor consent for "core" storage / ID modules (#8661) * Allow sharedId to work without vendor consent * Remove superfluous enforcement check from ixBidAdapter * Make core storage respect device access rules * respect storage access enforcement in userSync.js * UserID: check whether storage is enabled only once consent can be enforced * Add pubProvidedId * GDPR enforcement: enforce consent when data is not available, but GDPR module is enabled * Always enforce deviceAccess; move vendorless storage P1 enforcement behind `consentManagement.strictStorageEnforcement` --- modules/gdprEnforcement.js | 54 +++++++++--- modules/ixBidAdapter.js | 13 +-- modules/pubCommonId.js | 2 +- modules/sharedIdSystem.js | 8 +- modules/userId/index.js | 63 +++++++++----- src/storageManager.js | 21 ++--- src/userSync.js | 14 +-- test/helpers/consentData.js | 5 ++ test/spec/modules/adnuntiusBidAdapter_spec.js | 7 +- test/spec/modules/gdprEnforcement_spec.js | 87 ++++++++++++++++--- test/spec/modules/ixBidAdapter_spec.js | 1 + test/spec/modules/userId_spec.js | 10 ++- test/spec/unit/core/storageManager_spec.js | 29 ++++++- test/test_deps.js | 1 + 14 files changed, 220 insertions(+), 95 deletions(-) diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 9e300e1de97..b7035c509b6 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -12,6 +12,15 @@ import {validateStorageEnforcement} from '../src/storageManager.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; +// modules for which vendor consent is not needed (see https://github.com/prebid/Prebid.js/issues/8161) +const VENDORLESS_MODULES = new Set([ + 'sharedId', + 'pubCommonId', + 'pubProvidedId', +]); + +export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement'; + const TCF2 = { 'purpose1': { id: 1, name: 'storage' }, 'purpose2': { id: 2, name: 'basicAds' }, @@ -44,6 +53,7 @@ const biddersBlocked = []; const analyticsBlocked = []; let hooksAdded = false; +let strictStorageEnforcement = false; // Helps in stubbing these functions in unit tests. export const internal = { @@ -116,6 +126,18 @@ function getGvlidForAnalyticsAdapter(code) { return adapterManager.getAnalyticsAdapter(code) && (adapterManager.getAnalyticsAdapter(code).gvlid || null); } +export function shouldEnforce(consentData, purpose, name) { + if (consentData == null && gdprDataHandler.enabled) { + // there is no consent data, but the GDPR module has been installed and configured + // NOTE: this check is not foolproof, as when Prebid first loads, enforcement hooks have not been attached yet + // This piece of code would not run at all, and `gdprDataHandler.enabled` would be false, until the first + // `setConfig({consentManagement})` + logWarn(`Attempting operation that requires purpose ${purpose} consent while consent data is not available${name ? ` (module: ${name})` : ''}. Assuming no consent was given.`) + return true; + } + return consentData && consentData.gdprApplies; +} + /** * This function takes in a rule and consentData and validates against the consentData provided. Depending on what it returns, * the caller may decide to suppress a TCF-sensitive activity. @@ -123,9 +145,10 @@ function getGvlidForAnalyticsAdapter(code) { * @param {Object} consentData - gdpr consent data * @param {string=} currentModule - Bidder code of the current module * @param {number=} gvlId - GVL ID for the module + * @param vendorlessModule a predicate function that takes a module name, and returns true if the module does not need vendor consent * @returns {boolean} */ -export function validateRules(rule, consentData, currentModule, gvlId) { +export function validateRules(rule, consentData, currentModule, gvlId, vendorlessModule = VENDORLESS_MODULES.has.bind(VENDORLESS_MODULES)) { const purposeId = TCF2[Object.keys(TCF2).filter(purposeName => TCF2[purposeName].name === rule.purpose)[0]].id; // return 'true' if vendor present in 'vendorExceptions' @@ -143,7 +166,7 @@ export function validateRules(rule, consentData, currentModule, gvlId) { or the user has consented. Similar with vendors. */ const purposeAllowed = rule.enforcePurpose === false || purposeConsent === true; - const vendorAllowed = rule.enforceVendor === false || vendorConsent === true; + const vendorAllowed = vendorlessModule(currentModule) || rule.enforceVendor === false || vendorConsent === true; /* Few if any vendors should be declaring Legitimate Interest for Device Access (Purpose 1), but some are claiming @@ -160,20 +183,24 @@ export function validateRules(rule, consentData, currentModule, gvlId) { /** * This hook checks whether module has permission to access device or not. Device access include cookie and local storage * @param {Function} fn reference to original function (used by hook logic) + * @param isVendorless if true, do not require vendor consent (for e.g. core modules) * @param {Number=} gvlid gvlid of the module * @param {string=} moduleName name of the module + * @param result */ -export function deviceAccessHook(fn, gvlid, moduleName, result) { +export function deviceAccessHook(fn, isVendorless, gvlid, moduleName, result, {validate = validateRules} = {}) { result = Object.assign({}, { hasEnforcementHook: true }); if (!hasDeviceAccess()) { logWarn('Device access is disabled by Publisher'); result.valid = false; - fn.call(this, gvlid, moduleName, result); + } else if (isVendorless && !strictStorageEnforcement) { + // for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set + result.valid = true; } else { const consentData = gdprDataHandler.getConsentData(); - if (consentData && consentData.gdprApplies) { + if (shouldEnforce(consentData, 1, moduleName)) { const curBidder = config.getCurrentBidder(); // Bidders have a copy of storage object with bidder code binded. Aliases will also pass the same bidder code when invoking storage functions and hence if alias tries to access device we will try to grab the gvl id for alias instead of original bidder if (curBidder && (curBidder != moduleName) && adapterManager.aliasRegistry[curBidder] === moduleName) { @@ -182,21 +209,19 @@ export function deviceAccessHook(fn, gvlid, moduleName, result) { gvlid = getGvlid(moduleName) || gvlid; } const curModule = moduleName || curBidder; - let isAllowed = validateRules(purpose1Rule, consentData, curModule, gvlid); + let isAllowed = validate(purpose1Rule, consentData, curModule, gvlid, isVendorless ? () => true : undefined); if (isAllowed) { result.valid = true; - fn.call(this, gvlid, moduleName, result); } else { curModule && logWarn(`TCF2 denied device access for ${curModule}`); result.valid = false; storageBlocked.push(curModule); - fn.call(this, gvlid, moduleName, result); } } else { result.valid = true; - fn.call(this, gvlid, moduleName, result); } } + fn.call(this, isVendorless, gvlid, moduleName, result); } /** @@ -206,8 +231,8 @@ export function deviceAccessHook(fn, gvlid, moduleName, result) { */ export function userSyncHook(fn, ...args) { const consentData = gdprDataHandler.getConsentData(); - if (consentData && consentData.gdprApplies) { - const curBidder = config.getCurrentBidder(); + const curBidder = config.getCurrentBidder(); + if (shouldEnforce(consentData, 1, curBidder)) { const gvlid = getGvlid(curBidder); let isAllowed = validateRules(purpose1Rule, consentData, curBidder, gvlid); if (isAllowed) { @@ -228,7 +253,7 @@ export function userSyncHook(fn, ...args) { * @param {Object} consentData GDPR consent data */ export function userIdHook(fn, submodules, consentData) { - if (consentData && consentData.gdprApplies) { + if (shouldEnforce(consentData, 1, 'User ID')) { let userIdModules = submodules.map((submodule) => { const gvlid = getGvlid(submodule.submodule); const moduleName = submodule.submodule.name; @@ -255,7 +280,7 @@ export function userIdHook(fn, submodules, consentData) { */ export function makeBidRequestsHook(fn, adUnits, ...args) { const consentData = gdprDataHandler.getConsentData(); - if (consentData && consentData.gdprApplies) { + if (shouldEnforce(consentData, 2)) { adUnits.forEach(adUnit => { adUnit.bids = adUnit.bids.filter(bid => { const currBidder = bid.bidder; @@ -283,7 +308,7 @@ export function makeBidRequestsHook(fn, adUnits, ...args) { */ export function enableAnalyticsHook(fn, config) { const consentData = gdprDataHandler.getConsentData(); - if (consentData && consentData.gdprApplies) { + if (shouldEnforce(consentData, 7, 'Analytics')) { if (!isArray(config)) { config = [config] } @@ -341,6 +366,7 @@ export function setEnforcementConfig(config) { } else { enforcementRules = rules; } + strictStorageEnforcement = !!deepAccess(config, STRICT_STORAGE_ENFORCEMENT); purpose1Rule = find(enforcementRules, hasPurpose1); purpose2Rule = find(enforcementRules, hasPurpose2); diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index d4ac5208413..62ffba3d8dc 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -5,7 +5,6 @@ import { deepClone, deepSetValue, getGptSlotInfoForAdUnitCode, - hasDeviceAccess, inIframe, isArray, isEmpty, @@ -20,7 +19,7 @@ import { import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; import CONSTANTS from '../src/constants.json'; -import {getStorageManager, validateStorageEnforcement} from '../src/storageManager.js'; +import {getStorageManager} from '../src/storageManager.js'; import * as events from '../src/events.js'; import {find} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; @@ -1474,15 +1473,7 @@ function storeErrorEventData(data) { */ function localStorageHandler(data) { if (data.type === 'ERROR' && data.arguments && data.arguments[1] && data.arguments[1].bidder === BIDDER_CODE) { - const DEFAULT_ENFORCEMENT_SETTINGS = { - hasEnforcementHook: false, - valid: hasDeviceAccess() - }; - validateStorageEnforcement(GLOBAL_VENDOR_ID, BIDDER_CODE, DEFAULT_ENFORCEMENT_SETTINGS, (permissions) => { - if (permissions.valid) { - storeErrorEventData(data); - } - }); + storeErrorEventData(data); } } diff --git a/modules/pubCommonId.js b/modules/pubCommonId.js index faca59cce1c..e3fd21e7260 100644 --- a/modules/pubCommonId.js +++ b/modules/pubCommonId.js @@ -9,7 +9,7 @@ import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import { getStorageManager } from '../src/storageManager.js'; -const storage = getStorageManager(); +const storage = getStorageManager({moduleName: 'pubCommonId'}); const ID_NAME = '_pubcid'; const OPTOUT_NAME = '_pubcid_optout'; diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 656b62815c7..99a34ae17f4 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -10,8 +10,7 @@ import {submodule} from '../src/hook.js'; import { coppaDataHandler } from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; -const GVLID = 887; -export const storage = getStorageManager({gvlid: GVLID, moduleName: 'pubCommonId'}); +export const storage = getStorageManager({moduleName: 'pubCommonId'}); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const OPTOUT_NAME = '_pubcid_optout'; @@ -74,11 +73,6 @@ export const sharedIdSystemSubmodule = { */ name: 'sharedId', aliasName: 'pubCommonId', - /** - * Vendor id of prebid - * @type {Number} - */ - gvlid: GVLID, /** * decode the stored id value for passing to bid requests diff --git a/modules/userId/index.js b/modules/userId/index.js index ecc494846ee..f42c4c7827f 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -167,9 +167,6 @@ const CONSENT_DATA_COOKIE_STORAGE_CONFIG = { export const PBJS_USER_ID_OPTOUT_NAME = '_pbjs_id_optout'; export const coreStorage = getCoreStorageManager('userid'); -/** @type {string[]} */ -let validStorageTypes = []; - /** @type {boolean} */ let addedUserIdHook = false; @@ -834,7 +831,19 @@ function populateSubmoduleId(submodule, consentData, storedConsentData, forceRef } function initSubmodules(dest, submodules, consentData, forceRefresh = false) { - // gdpr consent with purpose one is required, otherwise exit immediately + if (!submodules.length) return []; // to simplify log messages from here on + + // filter out submodules whose storage type is not enabled + // this needs to be done here (after consent data has loaded) so that enforcement may disable storage globally + const storageTypes = getActiveStorageTypes(); + submodules = submodules.filter((submod) => !submod.config.storage || storageTypes.has(submod.config.storage.type)); + + if (!submodules.length) { + logWarn(`${MODULE_NAME} - no ID module is configured for one of the available storage types:`, Array.from(storageTypes)) + return []; + } + + // another consent check, this time each module is checked for consent with its own gvlid let { userIdModules, hasValidated } = validateGdprEnforcement(submodules, consentData); if (!hasValidated && !hasPurpose1Consent(consentData)) { logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`); @@ -885,7 +894,7 @@ function updateInitializedSubmodules(dest, submodule) { * @param {string[]} activeStorageTypes * @returns {SubmoduleConfig[]} */ -function getValidSubmoduleConfigs(configRegistry, submoduleRegistry, activeStorageTypes) { +function getValidSubmoduleConfigs(configRegistry, submoduleRegistry) { if (!Array.isArray(configRegistry)) { return []; } @@ -895,11 +904,11 @@ function getValidSubmoduleConfigs(configRegistry, submoduleRegistry, activeStora return carry; } // Validate storage config contains 'type' and 'name' properties with non-empty string values - // 'type' must be a value currently enabled in the browser + // 'type' must be one of html5, cookies if (config.storage && !isEmptyStr(config.storage.type) && !isEmptyStr(config.storage.name) && - activeStorageTypes.indexOf(config.storage.type) !== -1) { + ALL_STORAGE_TYPES.has(config.storage.type)) { carry.push(config); } else if (isPlainObject(config.value)) { carry.push(config); @@ -910,11 +919,33 @@ function getValidSubmoduleConfigs(configRegistry, submoduleRegistry, activeStora }, []); } +const ALL_STORAGE_TYPES = new Set([LOCAL_STORAGE, COOKIE]); + +function getActiveStorageTypes() { + const storageTypes = []; + let disabled = false; + if (coreStorage.localStorageIsEnabled()) { + storageTypes.push(LOCAL_STORAGE); + if (coreStorage.getDataFromLocalStorage(PBJS_USER_ID_OPTOUT_NAME)) { + logInfo(`${MODULE_NAME} - opt-out localStorage found, storage disabled`); + disabled = true; + } + } + if (coreStorage.cookiesAreEnabled()) { + storageTypes.push(COOKIE); + if (coreStorage.getCookie(PBJS_USER_ID_OPTOUT_NAME)) { + logInfo(`${MODULE_NAME} - opt-out cookie found, storage disabled`); + disabled = true; + } + } + return new Set(disabled ? [] : storageTypes) +} + /** * update submodules by validating against existing configs and storage types */ function updateSubmodules() { - const configs = getValidSubmoduleConfigs(configRegistry, submoduleRegistry, validStorageTypes); + const configs = getValidSubmoduleConfigs(configRegistry, submoduleRegistry); if (!configs.length) { return; } @@ -987,22 +1018,6 @@ export function init(config, {delay = GreedyPromise.timeout} = {}) { } submoduleRegistry = []; - // list of browser enabled storage types - validStorageTypes = [ - coreStorage.localStorageIsEnabled() ? LOCAL_STORAGE : null, - coreStorage.cookiesAreEnabled() ? COOKIE : null - ].filter(i => i !== null); - - // exit immediately if opt out cookie or local storage keys exists. - if (validStorageTypes.indexOf(COOKIE) !== -1 && coreStorage.getCookie(PBJS_USER_ID_OPTOUT_NAME)) { - logInfo(`${MODULE_NAME} - opt-out cookie found, exit module`); - return; - } - if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && coreStorage.getDataFromLocalStorage(PBJS_USER_ID_OPTOUT_NAME)) { - logInfo(`${MODULE_NAME} - opt-out localStorage found, exit module`); - return; - } - // listen for config userSyncs to be set configListener = config.getConfig('userSync', conf => { // Note: support for 'usersync' was dropped as part of Prebid.js 4.0 diff --git a/src/storageManager.js b/src/storageManager.js index e703e0774d3..4ab224f8d9b 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -1,6 +1,5 @@ import {hook} from './hook.js'; import {hasDeviceAccess, checkCookieSupport, logError, logInfo, isPlainObject} from './utils.js'; -import {includes} from './polyfill.js'; import {bidderSettings as defaultBidderSettings} from './bidderSettings.js'; const moduleTypeWhiteList = ['core', 'prebid-module']; @@ -33,13 +32,11 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = const storageAllowed = bidderSettings.get(bidderCode, 'storageAllowed'); return storageAllowed == null ? false : storageAllowed; } + + const isVendorless = moduleTypeWhiteList.includes(moduleType); + function isValid(cb) { - if (includes(moduleTypeWhiteList, moduleType)) { - let result = { - valid: true - } - return cb(result); - } else if (!isBidderAllowed()) { + if (!isBidderAllowed()) { logInfo(`bidderSettings denied access to device storage for bidder '${bidderCode}'`); const result = {valid: false}; return cb(result); @@ -48,7 +45,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = let hookDetails = { hasEnforcementHook: false } - validateStorageEnforcement(gvlid, bidderCode || moduleName, hookDetails, function(result) { + validateStorageEnforcement(isVendorless, gvlid, bidderCode || moduleName, hookDetails, function(result) { if (result && result.hasEnforcementHook) { value = cb(result); } else { @@ -149,11 +146,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = const cookiesAreEnabled = function (done) { let cb = function (result) { if (result && result.valid) { - if (checkCookieSupport()) { - return true; - } - window.document.cookie = 'prebid.cookieTest'; - return window.document.cookie.indexOf('prebid.cookieTest') !== -1; + return checkCookieSupport(); } return false; } @@ -303,7 +296,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = /** * This hook validates the storage enforcement if gdprEnforcement module is included */ -export const validateStorageEnforcement = hook('async', function(gvlid, moduleName, hookDetails, callback) { +export const validateStorageEnforcement = hook('async', function(isVendorless, gvlid, moduleName, hookDetails, callback) { callback(hookDetails); }, 'validateStorageEnforcement'); diff --git a/src/userSync.js b/src/userSync.js index 96c3d662cad..674114e11f6 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -315,12 +315,16 @@ export function newUserSync(userSyncDependencies) { return publicApi; } -const browserSupportsCookies = !isSafariBrowser() && storage.cookiesAreEnabled(); - -export const userSync = newUserSync({ +export const userSync = newUserSync(Object.defineProperties({ config: config.getConfig('userSync'), - browserSupportsCookies: browserSupportsCookies -}); +}, { + browserSupportsCookies: { + get: function() { + // call storage lazily to give time for consent data to be available + return !isSafariBrowser() && storage.cookiesAreEnabled(); + } + } +})); /** * @typedef {Object} UserSyncDependencies diff --git a/test/helpers/consentData.js b/test/helpers/consentData.js index b59388d67f2..c708e397bd6 100644 --- a/test/helpers/consentData.js +++ b/test/helpers/consentData.js @@ -2,6 +2,11 @@ import {gdprDataHandler} from 'src/adapterManager.js'; import {GreedyPromise} from '../../src/utils/promise.js'; export function mockGdprConsent(sandbox, getConsentData = () => null) { + sandbox.stub(gdprDataHandler, 'enabled').get(() => true) sandbox.stub(gdprDataHandler, 'promise').get(() => GreedyPromise.resolve(getConsentData())); sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(getConsentData) } + +beforeEach(() => { + gdprDataHandler.reset(); +}) diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 307830dd4ef..403ba63d7d1 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -11,8 +11,11 @@ describe('adnuntiusBidAdapter', function () { const GVLID = 855; const usi = utils.generateUUID() const meta = [{ key: 'usi', value: usi }] - const storage = getStorageManager({ gvlid: GVLID, moduleName: 'adnuntius' }) - storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) + + before(() => { + const storage = getStorageManager({gvlid: GVLID, moduleName: 'adnuntius'}) + storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) + }); beforeEach(function () { $$PREBID_GLOBAL$$.bidderSettings = { diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index a78f5ac948e..ddd522decab 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -10,13 +10,16 @@ import { purpose2Rule, enableAnalyticsHook, getGvlid, - internal + internal, STRICT_STORAGE_ENFORCEMENT } from 'modules/gdprEnforcement.js'; import { config } from 'src/config.js'; import adapterManager, { gdprDataHandler } from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; import { validateStorageEnforcement } from 'src/storageManager.js'; import * as events from 'src/events.js'; +import 'modules/appnexusBidAdapter.js'; // some tests expect this to be in the adapter registry +import 'src/prebid.js' +import {hook} from '../../../src/hook.js'; describe('gdpr enforcement', function () { let nextFnSpy; @@ -97,6 +100,10 @@ describe('gdpr enforcement', function () { } }; + before(() => { + hook.ready(); + }); + after(function () { validateStorageEnforcement.getHooks({ hook: deviceAccessHook }).remove(); $$PREBID_GLOBAL$$.requestBids.getHooks().remove(); @@ -143,13 +150,13 @@ describe('gdpr enforcement', function () { } }); - deviceAccessHook(nextFnSpy); + deviceAccessHook(nextFnSpy, false); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: false } - sinon.assert.calledWith(nextFnSpy, undefined, undefined, result); + sinon.assert.calledWith(nextFnSpy, false, undefined, undefined, result); }); it('should only check for consent for vendor exceptions when enforcePurpose and enforceVendor are false', function () { @@ -171,8 +178,8 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); - deviceAccessHook(nextFnSpy, 5, 'rubicon'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 5, 'rubicon'); expect(logWarnSpy.callCount).to.equal(0); }); @@ -194,8 +201,8 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); - deviceAccessHook(nextFnSpy, 3, 'rubicon'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 3, 'rubicon'); expect(logWarnSpy.callCount).to.equal(1); }); @@ -217,13 +224,13 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, 1, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, false, 1, 'appnexus', result); }); it('should use gvlMapping set by publisher', function() { @@ -248,13 +255,13 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, false, 4, 'appnexus', result); config.resetConfig(); }); @@ -283,16 +290,43 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, false, 4, 'appnexus', result); config.resetConfig(); curBidderStub.restore(); }); + + it(`should mark module as vendorless for rule validation when isVendorless = true and ${STRICT_STORAGE_ENFORCEMENT} is set`, () => { + setEnforcementConfig({ + [STRICT_STORAGE_ENFORCEMENT]: true + }); + let consentData = { + vendorData: staticConfig.consentData.getTCData, + gdprApplies: true + } + gdprDataHandlerStub.returns(consentData); + const validate = sinon.stub().callsFake(() => true); + deviceAccessHook(nextFnSpy, true, 123, 'mockModule', undefined, {validate}); + expect(validate.args[0][4]('mockModule')).to.be.true; + }); + + it(`should not enforce consent for vendorless modules if ${STRICT_STORAGE_ENFORCEMENT} is not set`, () => { + setEnforcementConfig({}); + let consentData = { + vendorData: staticConfig.consentData.getTCData, + gdprApplies: true + } + gdprDataHandlerStub.returns(consentData); + const validate = sinon.stub().callsFake(() => false); + deviceAccessHook(nextFnSpy, true, 123, 'mockModule', undefined, {validate}); + sinon.assert.callCount(validate, 0); + sinon.assert.calledWith(nextFnSpy, true, 123, 'mockModule', {hasEnforcementHook: true, valid: true}); + }) }); describe('userSyncHook', function () { @@ -888,6 +922,33 @@ describe('gdpr enforcement', function () { expect(isAllowed).to.equal(true); }); + describe('when module does not need vendor consent', () => { + Object.entries({ + 'storage': 1, + 'basicAds': 2, + 'measurement': 7 + }).forEach(([purpose, purposeNo]) => { + describe(`for purpose ${purpose}`, () => { + const rule = createGdprRule(purpose); + Object.entries({ + 'allowed': true, + 'not allowed': false + }).forEach(([t, consentGiven]) => { + it(`should be ${t} when purpose is ${t}`, () => { + const consent = utils.deepClone(consentData); + consent.vendorData.purpose.consents[purposeNo] = consentGiven; + // vendor consent (and gvlid) should be ignored + consent.vendorData.vendor.consents[123] = !consentGiven; + // take legitimate interest out of the picture for this test + consent.vendorData.purpose.legitimateInterests = {}; + const actual = validateRules(rule, consent, 'mockModule', 123, () => true); + expect(actual).to.equal(consentGiven); + }) + }) + }) + }) + }) + describe('Purpose 2 special case', function () { const consentDataWithLIFalse = utils.deepClone(consentData); consentDataWithLIFalse.vendorData.purpose.legitimateInterests['2'] = false; diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 086ec305ccc..243b702f03d 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -3462,6 +3462,7 @@ describe('IndexexchangeAdapter', function () { it('should not save error data into localstorage if consent is not given', () => { config.setConfig({ deviceAccess: false }); + storage.localStorageIsEnabled.restore(); // let core manage device access const bid = utils.deepClone(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0]); bid.params.size = ['400', 100]; expect(spec.isBidRequestValid(bid)).to.be.false; diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 486018d41c4..77ba1396afb 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -698,11 +698,15 @@ describe('User ID', function () { config.resetConfig(); }); - it('fails initialization if opt out cookie exists', function () { + it('does not fetch ids if opt out cookie exists', function () { init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - opt-out cookie found, exit module'); + const cfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + cfg.userSync.auctionDelay = 1; // to let init complete without an auction + config.setConfig(cfg); + return getGlobal().getUserIdsAsync().then((uid) => { + expect(uid).to.eql({}); + }) }); it('initializes if no opt out cookie exists', function () { diff --git a/test/spec/unit/core/storageManager_spec.js b/test/spec/unit/core/storageManager_spec.js index dacde5491b7..cb38aed9e47 100644 --- a/test/spec/unit/core/storageManager_spec.js +++ b/test/spec/unit/core/storageManager_spec.js @@ -3,7 +3,7 @@ import { getCoreStorageManager, storageCallbacks, getStorageManager, - newStorageManager + newStorageManager, validateStorageEnforcement } from 'src/storageManager.js'; import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; @@ -55,6 +55,33 @@ describe('storage manager', function() { deviceAccessSpy.restore(); }); + describe(`core storage`, () => { + let storage, validateHook; + + beforeEach(() => { + storage = getCoreStorageManager(); + validateHook = sinon.stub().callsFake(function (next, ...args) { + next.apply(this, args); + }); + validateStorageEnforcement.before(validateHook, 99); + }); + + afterEach(() => { + validateStorageEnforcement.getHooks({hook: validateHook}).remove(); + config.resetConfig(); + }) + + it('should respect (vendorless) consent enforcement', () => { + storage.localStorageIsEnabled(); + expect(validateHook.args[0][1]).to.eql(true); // isVendorless should be set to true + }); + + it('should respect the deviceAccess flag', () => { + config.setConfig({deviceAccess: false}); + expect(storage.localStorageIsEnabled()).to.be.false + }) + }) + describe('localstorage forbidden access in 3rd-party context', function() { let errorLogSpy; let originalLocalStorage; diff --git a/test/test_deps.js b/test/test_deps.js index 77fbad93e1c..35713106f8c 100644 --- a/test/test_deps.js +++ b/test/test_deps.js @@ -4,6 +4,7 @@ window.process = { } }; +require('test/helpers/consentData.js'); require('test/helpers/prebidGlobal.js'); require('test/mocks/adloaderStub.js'); require('test/mocks/xhr.js'); From 95ee4932dfbf699029cc9ebf3fa16e6fe723e8ac Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 28 Jul 2022 06:03:34 -0700 Subject: [PATCH 087/569] Topics FPD module: initial release (#8646) * Topics FPD module * Small improvements * Map taxonomyVersion to segtax, modelVersion to segclass * Convert fpdModule & topicsFpdModule to use GreedyPromise --- modules/.submodules.json | 3 +- modules/fpdModule/index.js | 22 +- modules/topicsFpdModule.js | 80 ++++++++ test/spec/modules/fpdModule_spec.js | 127 ++++++------ test/spec/modules/topicsFpdModule_spec.js | 239 ++++++++++++++++++++++ 5 files changed, 406 insertions(+), 65 deletions(-) create mode 100644 modules/topicsFpdModule.js create mode 100644 test/spec/modules/topicsFpdModule_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index d24e7ff96f5..b031a6bac02 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -70,7 +70,8 @@ ], "fpdModule": [ "enrichmentFpdModule", - "validationFpdModule" + "validationFpdModule", + "topicsFpdModule" ] }, "libraries": { diff --git a/modules/fpdModule/index.js b/modules/fpdModule/index.js index bd735e215a4..553cab47b66 100644 --- a/modules/fpdModule/index.js +++ b/modules/fpdModule/index.js @@ -4,6 +4,8 @@ */ import { config } from '../../src/config.js'; import { module, getHook } from '../../src/hook.js'; +import {logError} from '../../src/utils.js'; +import {GreedyPromise} from '../../src/utils/promise.js'; let submodules = []; @@ -17,19 +19,27 @@ export function reset() { export function processFpd({global = {}, bidder = {}} = {}) { let modConf = config.getConfig('firstPartyData') || {}; - + let result = GreedyPromise.resolve({global, bidder}); submodules.sort((a, b) => { return ((a.queue || 1) - (b.queue || 1)); }).forEach(submodule => { - ({global = global, bidder = bidder} = submodule.processFpd(modConf, {global, bidder})); + result = result.then( + ({global, bidder}) => GreedyPromise.resolve(submodule.processFpd(modConf, {global, bidder})) + .catch((err) => { + logError(`Error in FPD module ${submodule.name}`, err); + return {}; + }) + .then((result) => ({global: result.global || global, bidder: result.bidder || bidder})) + ); }); - - return {global, bidder}; + return result; } export function startAuctionHook(fn, req) { - Object.assign(req.ortb2Fragments, processFpd(req.ortb2Fragments)); - fn.call(this, req); + processFpd(req.ortb2Fragments).then((ortb2Fragments) => { + Object.assign(req.ortb2Fragments, ortb2Fragments); + fn.call(this, req); + }) } function setupHook() { diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js new file mode 100644 index 00000000000..dbd1d3e90e9 --- /dev/null +++ b/modules/topicsFpdModule.js @@ -0,0 +1,80 @@ +import {logError, logWarn, mergeDeep} from '../src/utils.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {submodule} from '../src/hook.js'; +import {GreedyPromise} from '../src/utils/promise.js'; + +const TAXONOMIES = { + // map from topic taxonomyVersion to IAB segment taxonomy + '1': 600 +} + +function partitionBy(field, items) { + return items.reduce((partitions, item) => { + const key = item[field]; + if (!partitions.hasOwnProperty(key)) partitions[key] = []; + partitions[key].push(item); + return partitions; + }, {}); +} + +export function getTopicsData(name, topics, taxonomies = TAXONOMIES) { + return Object.entries(partitionBy('taxonomyVersion', topics)) + .filter(([taxonomyVersion]) => { + if (!taxonomies.hasOwnProperty(taxonomyVersion)) { + logWarn(`Unrecognized taxonomyVersion from Topics API: "${taxonomyVersion}"; topic will be ignored`); + return false; + } + return true; + }).flatMap(([taxonomyVersion, topics]) => + Object.entries(partitionBy('modelVersion', topics)) + .map(([modelVersion, topics]) => { + const datum = { + ext: { + segtax: taxonomies[taxonomyVersion], + segclass: modelVersion + }, + segment: topics.map((topic) => ({id: topic.topic.toString()})) + }; + if (name != null) { + datum.name = name; + } + return datum; + }) + ); +} + +export function getTopics(doc = document) { + let topics = null; + try { + if ('browsingTopics' in doc && doc.featurePolicy.allowsFeature('browsing-topics')) { + topics = GreedyPromise.resolve(doc.browsingTopics()); + } + } catch (e) { + logError('Could not call topics API', e); + } + if (topics == null) { + topics = GreedyPromise.resolve([]); + } + return topics; +} + +const topicsData = getTopics().then((topics) => getTopicsData(getRefererInfo().domain, topics)); + +export function processFpd(config, {global}, {data = topicsData} = {}) { + return data.then((data) => { + if (data.length) { + mergeDeep(global, { + user: { + data + } + }); + } + return {global}; + }); +} + +submodule('firstPartyData', { + name: 'topics', + queue: 1, + processFpd +}); diff --git a/test/spec/modules/fpdModule_spec.js b/test/spec/modules/fpdModule_spec.js index d43ae8309bd..498bed29243 100644 --- a/test/spec/modules/fpdModule_spec.js +++ b/test/spec/modules/fpdModule_spec.js @@ -5,9 +5,6 @@ import {processFpd, registerSubmodules, startAuctionHook, reset} from 'modules/f import * as enrichmentModule from 'modules/enrichmentFpdModule.js'; import * as validationModule from 'modules/validationFpdModule/index.js'; -let enrichments = {...enrichmentModule}; -let validations = {...validationModule}; - describe('the first party data module', function () { afterEach(function () { config.resetConfig(); @@ -18,21 +15,37 @@ describe('the first party data module', function () { global: {key: 'value'}, bidder: {A: {bkey: 'bvalue'}} } - before(() => { + beforeEach(() => { reset(); + }); + + it('should run ortb2Fragments through fpd submodules', () => { registerSubmodules({ name: 'test', - queue: 2, processFpd: function () { return mockFpd; } }); - }) + const req = {ortb2Fragments: {}}; + return new Promise((resolve) => startAuctionHook(resolve, req)) + .then(() => { + expect(req.ortb2Fragments).to.eql(mockFpd); + }) + }); - it('should run ortb2Fragments through fpd submodules', () => { + it('should work with fpd submodules that return promises', () => { + registerSubmodules({ + name: 'test', + processFpd: function () { + return Promise.resolve(mockFpd); + } + }); const req = {ortb2Fragments: {}}; - startAuctionHook(() => null, req); - expect(req.ortb2Fragments).to.eql(mockFpd); + return new Promise((resolve) => { + startAuctionHook(resolve, req); + }).then(() => { + expect(req.ortb2Fragments).to.eql(mockFpd); + }); }); }); @@ -79,7 +92,6 @@ describe('the first party data module', function () { }); it('filters ortb2 data that is set', function () { - let validated; const global = { user: { data: {}, @@ -113,42 +125,42 @@ describe('the first party data module', function () { width = 1120; height = 750; - ({global: validated} = processFpd({global})); - expect(validated.site.ref).to.equal(getRefererInfo().ref || undefined); - expect(validated.site.page).to.equal('https://www.domain.com/path?query=12345'); - expect(validated.site.domain).to.equal('domain.com'); - expect(validated.site.content.data).to.deep.equal([{segment: [{id: 'test'}], name: 'bar'}]); - expect(validated.user.data).to.be.undefined; - expect(validated.device).to.deep.to.equal({w: 1, h: 1}); - expect(validated.site.keywords).to.be.undefined; + return processFpd({global}).then(({global: validated}) => { + expect(validated.site.ref).to.equal(getRefererInfo().ref || undefined); + expect(validated.site.page).to.equal('https://www.domain.com/path?query=12345'); + expect(validated.site.domain).to.equal('domain.com'); + expect(validated.site.content.data).to.deep.equal([{segment: [{id: 'test'}], name: 'bar'}]); + expect(validated.user.data).to.be.undefined; + expect(validated.device).to.deep.to.equal({w: 1, h: 1}); + expect(validated.site.keywords).to.be.undefined; + }); }); it('should not overwrite existing data with default settings', function () { - let validated; const global = { site: { ref: 'https://referer.com' } }; - ({global: validated} = processFpd({global})); - expect(validated.site.ref).to.equal('https://referer.com'); + return processFpd({global}).then(({global: validated}) => { + expect(validated.site.ref).to.equal('https://referer.com'); + }); }); it('should allow overwrite default data with setConfig', function () { - let validated; const global = { site: { ref: 'https://referer.com' } }; - ({global: validated} = processFpd({global})); - expect(validated.site.ref).to.equal('https://referer.com'); + return processFpd({global}).then(({global: validated}) => { + expect(validated.site.ref).to.equal('https://referer.com'); + }); }); it('should filter all data', function () { - let validated; let global = { imp: [], site: { @@ -179,15 +191,13 @@ describe('the first party data module', function () { adServerCurrency: 'USD' } }; - config.setConfig({'firstPartyData': {skipEnrichments: true}}); - - ({global: validated} = processFpd({global})); - expect(validated).to.deep.equal({}); + return processFpd({global}).then(({global: validated}) => { + expect(validated).to.deep.equal({}); + }); }); it('should add enrichments but not alter any arbitrary ortb2 data', function () { - let validated; let global = { site: { ext: { @@ -205,12 +215,12 @@ describe('the first party data module', function () { }, cur: ['USD'] }; - - ({global: validated} = processFpd({global})); - expect(validated.site.ref).to.equal(getRefererInfo().referer); - expect(validated.site.ext.data).to.deep.equal({inventory: ['value1']}); - expect(validated.user.ext.data).to.deep.equal({visitor: ['value2']}); - expect(validated.cur).to.deep.equal(['USD']); + return processFpd({global}).then(({global: validated}) => { + expect(validated.site.ref).to.equal(getRefererInfo().referer); + expect(validated.site.ext.data).to.deep.equal({inventory: ['value1']}); + expect(validated.user.ext.data).to.deep.equal({visitor: ['value2']}); + expect(validated.cur).to.deep.equal(['USD']); + }) }); it('should filter bidderConfig data', function () { @@ -230,12 +240,13 @@ describe('the first party data module', function () { } }; - const {bidder: validated} = processFpd({bidder}); - expect(validated.bidderA).to.not.be.undefined; - expect(validated.bidderA.user.data).to.be.undefined; - expect(validated.bidderA.user.keywords).to.equal('test'); - expect(validated.bidderA.site.keywords).to.equal('other'); - expect(validated.bidderA.site.ref).to.equal('https://domain.com'); + return processFpd({bidder}).then(({bidder: validated}) => { + expect(validated.bidderA).to.not.be.undefined; + expect(validated.bidderA.user.data).to.be.undefined; + expect(validated.bidderA.user.keywords).to.equal('test'); + expect(validated.bidderA.site.keywords).to.equal('other'); + expect(validated.bidderA.site.ref).to.equal('https://domain.com'); + }) }); it('should not filter bidderConfig data as it is valid', function () { @@ -255,17 +266,16 @@ describe('the first party data module', function () { } }; - const {bidder: validated} = processFpd({bidder}); - - expect(validated.bidderA).to.not.be.undefined; - expect(validated.bidderA.user.data).to.deep.equal([{segment: [{id: 'data1_id'}], name: 'data1'}]); - expect(validated.bidderA.user.keywords).to.equal('test'); - expect(validated.bidderA.site.keywords).to.equal('other'); - expect(validated.bidderA.site.ref).to.equal('https://domain.com'); + return processFpd({bidder}).then(({bidder: validated}) => { + expect(validated.bidderA).to.not.be.undefined; + expect(validated.bidderA.user.data).to.deep.equal([{segment: [{id: 'data1_id'}], name: 'data1'}]); + expect(validated.bidderA.user.keywords).to.equal('test'); + expect(validated.bidderA.site.keywords).to.equal('other'); + expect(validated.bidderA.site.ref).to.equal('https://domain.com'); + }); }); it('should not set default values if skipEnrichments is turned on', function () { - let validated; config.setConfig({'firstPartyData': {skipEnrichments: true}}); let global = { @@ -281,15 +291,15 @@ describe('the first party data module', function () { } }; - ({global: validated} = processFpd({global})); - expect(validated.device).to.be.undefined; - expect(validated.site.ref).to.be.undefined; - expect(validated.site.page).to.be.undefined; - expect(validated.site.domain).to.be.undefined; + return processFpd({global}).then(({global: validated}) => { + expect(validated.device).to.be.undefined; + expect(validated.site.ref).to.be.undefined; + expect(validated.site.page).to.be.undefined; + expect(validated.site.domain).to.be.undefined; + }); }); it('should not validate ortb2 data if skipValidations is turned on', function () { - let validated; config.setConfig({'firstPartyData': {skipValidations: true}}); let global = { @@ -304,8 +314,9 @@ describe('the first party data module', function () { } }; - ({global: validated} = processFpd({global})); - expect(validated.user.data).to.deep.equal([{segment: [{id: 'nonfiltered'}]}]); + return processFpd({global}).then(({global: validated}) => { + expect(validated.user.data).to.deep.equal([{segment: [{id: 'nonfiltered'}]}]); + }); }); }); }); diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js new file mode 100644 index 00000000000..3781768497b --- /dev/null +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -0,0 +1,239 @@ +import {getTopics, getTopicsData, processFpd} from '../../../modules/topicsFpdModule.js'; +import {deepClone} from '../../../src/utils.js'; + +describe('getTopicsData', () => { + function makeTopic(topic, modelv, taxv = '1') { + return { + topic, + taxonomyVersion: taxv, + modelVersion: modelv + } + } + + function byTaxClass(segments) { + return segments.reduce((memo, segment) => { + memo[`${segment.ext.segtax}:${segment.ext.segclass}`] = segment; + return memo; + }, {}) + } + + [ + { + t: 'no topics', + topics: [], + expected: [] + }, + { + t: 'single topic', + topics: [makeTopic(123, 'm1')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + } + ] + }, + { + t: 'multiple topics with the same model version', + topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'}, + {id: '321'} + ] + } + ] + }, + { + t: 'multiple topics with different model versions', + topics: [makeTopic(1, 'm1'), makeTopic(2, 'm1'), makeTopic(3, 'm2')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '1'}, + {id: '2'} + ] + }, + { + ext: { + segtax: 600, + segclass: 'm2' + }, + segment: [ + {id: '3'} + ] + } + ] + }, + { + t: 'multiple topics, some with a taxonomy version other than "1"', + topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1', 'other')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + } + ] + }, + { + t: 'multiple topics in multiple taxonomies', + taxonomies: { + '1': 600, + '2': 601 + }, + topics: [ + makeTopic(123, 'm1', '1'), + makeTopic(321, 'm1', '2'), + makeTopic(213, 'm2', '1'), + ], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + }, + { + ext: { + segtax: 601, + segclass: 'm1', + }, + segment: [ + {id: '321'} + ] + }, + { + ext: { + segtax: 600, + segclass: 'm2' + }, + segment: [ + {id: '213'} + ] + } + ] + } + ].forEach(({t, topics, expected, taxonomies}) => { + describe(`on ${t}`, () => { + it('should convert topics to user.data segments correctly', () => { + const actual = getTopicsData('mockName', topics, taxonomies); + expect(actual.length).to.eql(expected.length); + expected = byTaxClass(expected); + Object.entries(byTaxClass(actual)).forEach(([key, datum]) => { + sinon.assert.match(datum, expected[key]); + expect(datum.name).to.equal('mockName'); + }) + }); + + it('should not set name if null', () => { + getTopicsData(null, topics).forEach((data) => { + expect(data.hasOwnProperty('name')).to.be.false; + }) + }) + }) + }) +}); + +describe('getTopics', () => { + Object.entries({ + 'document with no browsingTopics': {}, + 'document that disallows topics': { + featurePolicy: { + allowsFeature: sinon.stub().returns(false) + } + }, + 'document that throws on featurePolicy': { + browsingTopics: sinon.stub(), + get featurePolicy() { + throw new Error() + } + }, + 'document that throws on browsingTopics': { + browsingTopics: sinon.stub().callsFake(() => { throw new Error(); }), + featurePolicy: { + allowsFeature: sinon.stub().returns(true) + } + }, + }).forEach(([t, doc]) => { + it(`should resolve to an empty list on ${t}`, () => { + return getTopics(doc).then((topics) => { + expect(topics).to.eql([]); + }); + }) + }); + + it('should call `document.browsingTopics` when allowed', () => { + const topics = ['t1', 't2'] + return getTopics({ + browsingTopics: sinon.stub().returns(Promise.resolve(topics)), + featurePolicy: { + allowsFeature: sinon.stub().returns(true) + } + }).then((actual) => { + expect(actual).to.eql(topics); + }) + }) +}) + +describe('processFpd', () => { + const mockData = [ + { + name: 'domain', + segment: [{id: 123}] + }, + { + name: 'domain', + segment: [{id: 321}] + } + ]; + + it('should add topics data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) + .then(({global}) => { + expect(global.user.data).to.eql(mockData); + }); + }); + + it('should apppend to existing user.data', () => { + const global = { + user: { + data: [ + {name: 'preexisting'}, + ] + } + }; + return processFpd({}, {global: deepClone(global)}, {data: Promise.resolve(mockData)}) + .then((data) => { + expect(data.global.user.data).to.eql(global.user.data.concat(mockData)); + }); + }); + + it('should not modify fpd when there is no data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve([])}) + .then((data) => { + expect(data.global).to.eql({}); + }); + }); +}); From 45c33129674e3015ebfee45aa6f8b06410fab0c6 Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Thu, 28 Jul 2022 22:08:06 +0900 Subject: [PATCH 088/569] Relaido Bid Adapter: Add params for hashed canonical url. (#8743) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * Add params for hashed canonical url. * Add params for hashed canonical url. removed review comment. Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun Co-authored-by: n.maeura --- modules/relaidoBidAdapter.js | 15 ++++++++++++--- test/spec/modules/relaidoBidAdapter_spec.js | 4 +++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 880e826d75b..c9e9fc00eb7 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -3,10 +3,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { getStorageManager } from '../src/storageManager.js'; +import sha1 from 'crypto-js/sha1'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.8'; +const ADAPTER_VERSION = '1.0.9'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -104,9 +105,9 @@ function buildRequests(validBidRequests, bidderRequest) { uuid: getUuid(), pv: '$prebid.version$', imuid: imuid, - // TODO: is 'page' the right value here? + canonical_url_hash: getCanonicalUrlHash(bidderRequest.refererInfo), ref: bidderRequest.refererInfo.page - }) + }); return { method: 'POST', @@ -277,6 +278,14 @@ function getUuid() { return newId; } +function getCanonicalUrlHash(refererInfo) { + const canonicalUrl = refererInfo.canonicalUrl || null; + if (!canonicalUrl) { + return null; + } + return sha1(canonicalUrl).toString(); +} + export function isMobile() { const ua = navigator.userAgent; if (ua.indexOf('iPhone') > -1 || ua.indexOf('iPod') > -1 || (ua.indexOf('Android') > -1 && ua.indexOf('Tablet') == -1)) { diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index ead44f96bc9..9aec1f179a4 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -51,7 +51,8 @@ describe('RelaidoAdapter', function () { bidderRequest = { timeout: 1000, refererInfo: { - page: 'https://publisher.com/home' + page: 'https://publisher.com/home?aaa=test1&bbb=test2', + canonicalUrl: 'https://publisher.com/home' } }; serverResponse = { @@ -238,6 +239,7 @@ describe('RelaidoAdapter', function () { const request = data.bids[0]; expect(bidRequests.method).to.equal('POST'); expect(bidRequests.url).to.equal('https://api.relaido.jp/bid/v1/sprebid'); + expect(data.canonical_url_hash).to.equal('e6092f44a0044903ae3764126eedd6187c1d9f04'); expect(data.ref).to.equal(bidderRequest.refererInfo.page); expect(data.timeout_ms).to.equal(bidderRequest.timeout); expect(request.ad_unit_code).to.equal(bidRequest.adUnitCode); From 93fc10b6deb5c87dc5f7880baa2c3971ed79024e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Jul 2022 11:41:30 -0400 Subject: [PATCH 089/569] Bump terser from 5.12.0 to 5.14.2 (#8719) Bumps [terser](https://github.com/terser/terser) from 5.12.0 to 5.14.2. - [Release notes](https://github.com/terser/terser/releases) - [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md) - [Commits](https://github.com/terser/terser/commits) --- updated-dependencies: - dependency-name: terser dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 103 +++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index ccf4d92a711..ef3ab0394fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1922,6 +1922,20 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", @@ -1930,15 +1944,34 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -21266,14 +21299,14 @@ } }, "node_modules/terser": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.0.tgz", - "integrity": "sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "dependencies": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "bin": { @@ -21387,15 +21420,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", @@ -24729,20 +24753,47 @@ } } }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@jridgewell/resolve-uri": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==" }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@jridgewell/sourcemap-codec": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -40160,14 +40211,14 @@ } }, "terser": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.0.tgz", - "integrity": "sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "requires": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "dependencies": { @@ -40182,12 +40233,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true } } }, From 7e203b39f0395ee35b37de1510052cfe59bd7bdb Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 28 Jul 2022 10:37:38 -0700 Subject: [PATCH 090/569] Prebid core: fix native trackers for Prebid Server; simplify native ORTB logic (#8748) * Prebid core: simplify native ORTB logic * Remove nativeMapper; do not mix ORTB request and response * fix lint * set bidRequest.nativeParams after ortb -> legacy conversion * Fix native trackers for native ortb responses (including Prebid Server) * remove redundant native request generation from PBS adapter * use includes instead of in --- modules/prebidServerBidAdapter/index.js | 118 +-------- src/adapterManager.js | 1 + src/native.js | 235 ++++++++++-------- .../modules/prebidServerBidAdapter_spec.js | 99 ++++---- test/spec/native_spec.js | 145 ++++++++++- test/spec/unit/core/bidderFactory_spec.js | 3 + 6 files changed, 336 insertions(+), 265 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 9c240615c04..9c7c3c921b0 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -2,7 +2,6 @@ import Adapter from '../../src/adapter.js'; import {createBid} from '../../src/bidfactory.js'; import { bind, - cleanObj, createTrackPixelHtml, deepAccess, deepClone, @@ -30,17 +29,16 @@ import { } from '../../src/utils.js'; import CONSTANTS from '../../src/constants.json'; import adapterManager from '../../src/adapterManager.js'; -import { config } from '../../src/config.js'; -import { VIDEO, NATIVE } from '../../src/mediaTypes.js'; -import { isValid } from '../../src/adapters/bidderFactory.js'; +import {config} from '../../src/config.js'; +import {NATIVE, VIDEO} from '../../src/mediaTypes.js'; +import {isValid} from '../../src/adapters/bidderFactory.js'; import * as events from '../../src/events.js'; import {find, includes} from '../../src/polyfill.js'; -import { S2S_VENDORS } from './config.js'; -import { ajax } from '../../src/ajax.js'; +import {S2S_VENDORS} from './config.js'; +import {ajax} from '../../src/ajax.js'; import {hook} from '../../src/hook.js'; import {getGlobal} from '../../src/prebidGlobal.js'; import {hasPurpose1Consent} from '../../src/utils/gpdr.js'; -import { nativeMapper } from '../../src/native.js'; const getConfig = config.getConfig; @@ -446,11 +444,6 @@ if (FEATURES.NATIVE) { }); }); } -/* - * Protocol spec for OpenRTB endpoint - * e.g., https:///v1/openrtb2/auction - */ -let nativeAssetCache = {}; // store processed native params to preserve /** * map wurl to auction id and adId for use in the BID_WON event @@ -552,78 +545,6 @@ Object.assign(ORTB2.prototype, { impIds.add(impressionId); this.adUnitsByImp[impressionId] = adUnit; - const nativeParams = adUnit.nativeParams; - let nativeAssets = nativeAssetCache[impressionId] = deepAccess(nativeParams, 'ortb.assets'); - if (FEATURES.NATIVE && nativeParams && !nativeAssets) { - let idCounter = -1; - try { - nativeAssets = nativeAssetCache[impressionId] = Object.keys(nativeParams).reduce((assets, type) => { - let params = nativeParams[type]; - - function newAsset(obj) { - idCounter++; - return Object.assign({ - required: params.required ? 1 : 0, - id: (isNumber(params.id)) ? idCounter = params.id : idCounter - }, obj ? cleanObj(obj) : {}); - } - - switch (type) { - case 'image': - case 'icon': - let imgTypeId = nativeImgIdMap[type]; - let asset = cleanObj({ - type: imgTypeId, - w: deepAccess(params, 'sizes.0'), - h: deepAccess(params, 'sizes.1'), - wmin: deepAccess(params, 'aspect_ratios.0.min_width'), - hmin: deepAccess(params, 'aspect_ratios.0.min_height') - }); - if (!((asset.w && asset.h) || (asset.hmin && asset.wmin))) { - throw 'invalid img sizes (must provide sizes or min_height & min_width if using aspect_ratios)'; - } - if (Array.isArray(params.aspect_ratios)) { - // pass aspect_ratios as ext data I guess? - const aspectRatios = params.aspect_ratios - .filter((ar) => ar.ratio_width && ar.ratio_height) - .map(ratio => `${ratio.ratio_width}:${ratio.ratio_height}`); - if (aspectRatios.length > 0) { - asset.ext = { - aspectratios: aspectRatios - } - } - } - assets.push(newAsset({ - img: asset - })); - break; - case 'title': - if (!params.len) { - throw 'invalid title.len'; - } - assets.push(newAsset({ - title: { - len: params.len - } - })); - break; - default: - let dataAssetTypeId = nativeDataIdMap[type]; - if (dataAssetTypeId) { - assets.push(newAsset({ - data: { - type: dataAssetTypeId, - len: params.len - } - })) - } - } - return assets; - }, []); - } catch (e) { - logError('error creating native request: ' + String(e)) - } - } const videoParams = deepAccess(adUnit, 'mediaTypes.video'); const bannerParams = deepAccess(adUnit, 'mediaTypes.banner'); @@ -678,8 +599,8 @@ Object.assign(ORTB2.prototype, { }, {}); } } - - if (FEATURES.NATIVE && nativeAssets) { + const nativeReq = deepAccess(adUnit, 'nativeOrtbRequest') + if (FEATURES.NATIVE && nativeReq) { const defaultRequest = { // TODO: determine best way to pass these and if we allow defaults context: 1, @@ -689,18 +610,13 @@ Object.assign(ORTB2.prototype, { ], // TODO: figure out how to support privacy field // privacy: int - assets: nativeAssets }; - const ortbRequest = deepAccess(nativeParams, 'ortb'); try { - const request = ortbRequest ? Object.assign(defaultRequest, ortbRequest) : defaultRequest; + const request = Object.assign(defaultRequest, nativeReq); mediaTypes[NATIVE] = { request: JSON.stringify(request), ver: '1.2' }; - // saving the converted ortb native request into the native mapper, so the Universal Creative - // can render the native ad directly. - adUnit.bids.forEach(bid => nativeMapper.set(bid.bid_id, request)); } catch (e) { logError('error creating native request: ' + String(e)); } @@ -1091,24 +1007,6 @@ Object.assign(ORTB2.prototype, { ortb = bidObject.adm = bid.adm; } - // ortb.imptrackers and ortb.jstracker are going to be deprecated. So, when we find - // those properties, we're creating the equivalent eventtrackers and let prebid universal - // creative deal with it - for (const imptracker of ortb.imptrackers || []) { - ortb.eventtrackers.push({ - event: nativeEventTrackerEventMap.impression, - method: nativeEventTrackerMethodMap.img, - url: imptracker - }) - } - if (ortb.jstracker) { - ortb.eventtrackers.push({ - event: nativeEventTrackerEventMap.impression, - method: nativeEventTrackerMethodMap.js, - url: ortb.jstracker - }) - } - if (isPlainObject(ortb) && Array.isArray(ortb.assets)) { bidObject.native = { ortb, diff --git a/src/adapterManager.js b/src/adapterManager.js index a707cc473d6..03f3848e81b 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -66,6 +66,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src}) { .reduce((bids, bid) => { bid = Object.assign({}, bid, getDefinedParams(adUnit, [ 'nativeParams', + 'nativeOrtbRequest', 'ortb2Imp', 'mediaType', 'renderer' diff --git a/src/native.js b/src/native.js index 11fb0b54b90..4ba4505d441 100644 --- a/src/native.js +++ b/src/native.js @@ -1,9 +1,21 @@ -import { deepAccess, getKeyByValue, insertHtmlIntoIframe, isInteger, isNumber, isPlainObject, logError, triggerPixel, isBoolean, isArray, deepClone } from './utils.js'; +import { + deepAccess, + deepClone, + getKeyByValue, + insertHtmlIntoIframe, + isArray, + isBoolean, + isInteger, + isNumber, + isPlainObject, + logError, + triggerPixel, + pick +} from './utils.js'; import {includes} from './polyfill.js'; import {auctionManager} from './auctionManager.js'; import CONSTANTS from './constants.json'; -import { NATIVE } from './mediaTypes.js'; -import { filters } from './targeting.js'; +import {NATIVE} from './mediaTypes.js'; export const nativeAdapters = []; @@ -74,7 +86,20 @@ const { NATIVE_ASSET_TYPES, NATIVE_IMAGE_TYPES, PREBID_NATIVE_DATA_KEYS_TO_ORTB, const PREBID_NATIVE_DATA_KEYS_TO_ORTB_INVERSE = inverse(PREBID_NATIVE_DATA_KEYS_TO_ORTB); const NATIVE_ASSET_TYPES_INVERSE = inverse(NATIVE_ASSET_TYPES); -export const nativeMapper = new Map(); +const TRACKER_METHODS = { + img: 1, + js: 2, + 1: 'img', + 2: 'js' +} + +const TRACKER_EVENTS = { + impression: 1, + 'viewable-mrc50': 2, + 'viewable-mrc100': 3, + 'viewable-video50': 4, +} + /** * Recieves nativeParams from an adUnit. If the params were not of type 'type', * passes them on directly. If they were of type 'type', translate @@ -98,6 +123,9 @@ export function decorateAdUnitsWithNativeParams(adUnits) { if (nativeParams) { adUnit.nativeParams = processNativeAdUnitParams(nativeParams); } + if (adUnit.nativeParams) { + adUnit.nativeOrtbRequest = adUnit.nativeParams.ortb || toOrtbNativeRequest(adUnit.nativeParams); + } }); } export function isOpenRTBBidRequestValid(ortb) { @@ -192,10 +220,8 @@ export const hasNonNativeBidder = adUnit => export function nativeBidIsValid(bid, {index = auctionManager.index} = {}) { const adUnit = index.getAdUnit(bid); if (!adUnit) { return false; } - let ortbRequest = - adUnit?.nativeParams?.ortb || nativeMapper.get(bid.requestId) || toOrtbNativeRequest(adUnit.nativeParams); - let ortbResponse = - bid.native?.ortb || toOrtbNativeResponse(bid.native, ortbRequest); + let ortbRequest = adUnit.nativeOrtbRequest + let ortbResponse = bid.native?.ortb || toOrtbNativeResponse(bid.native, ortbRequest); return isNativeOpenRTBBidValid(ortbResponse, ortbRequest); } @@ -242,20 +268,45 @@ export function isNativeOpenRTBBidValid(bidORTB, bidRequestORTB) { * fireTrackers(); // fires impressions when creative is loaded * */ -export function fireNativeTrackers(message, adObject) { - let trackers; +export function fireNativeTrackers(message, bidResponse) { + const nativeResponse = bidResponse.native.ortb || legacyPropertiesToOrtbNative(bidResponse.native); + if (message.action === 'click') { - trackers = adObject['native'] && adObject['native'].clickTrackers; + fireClickTrackers(nativeResponse); } else { - trackers = adObject['native'] && adObject['native'].impressionTrackers; + fireImpressionTrackers(nativeResponse); + } + return message.action; +} - if (adObject['native'] && adObject['native'].javascriptTrackers) { - insertHtmlIntoIframe(adObject['native'].javascriptTrackers); +export function fireImpressionTrackers(nativeResponse, {runMarkup = (mkup) => insertHtmlIntoIframe(mkup), fetchURL = triggerPixel} = {}) { + const impTrackers = (nativeResponse.eventtrackers || []) + .filter(tracker => tracker.event === TRACKER_EVENTS.impression); + + let {img, js} = impTrackers.reduce((tally, tracker) => { + if (TRACKER_METHODS.hasOwnProperty(tracker.method)) { + tally[TRACKER_METHODS[tracker.method]].push(tracker.url) } + return tally; + }, {img: [], js: []}); + + if (nativeResponse.imptrackers) { + img = img.concat(nativeResponse.imptrackers); } + img.forEach(url => fetchURL(url)); - (trackers || []).forEach(triggerPixel); - return message.action; + js = js.map(url => ``); + if (nativeResponse.jstracker) { + // jstracker is already HTML markup + js = js.concat([nativeResponse.jstracker]); + } + if (js.length) { + runMarkup(js.join('\n')); + } +} + +export function fireClickTrackers(nativeResponse, {fetchURL = triggerPixel} = {}) { + (nativeResponse.link?.clicktrackers || []).forEach(url => fetchURL(url)); } /** @@ -342,16 +393,15 @@ export function getAssetMessage(data, adObject) { return message; } -export function getAllAssetsMessage(data, adObject) { +export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse) => auctionManager.index.getAdUnit(bidResponse).nativeOrtbRequest} = {}) { const message = { message: 'assetResponse', adId: data.adId, }; // Pass to Prebid Universal Creative all assets, the legacy ones + the ortb ones (under ortb property) - const ortbRequest = nativeMapper.get(adObject.requestId); + const ortbRequest = getNativeReq(adObject); let nativeReq = adObject.native; - nativeMapper.delete(adObject.requestId); const ortbResponse = adObject.native?.ortb; let legacyResponse = {}; if (ortbRequest && ortbResponse) { @@ -384,7 +434,6 @@ export function getAllAssetsMessage(data, adObject) { message.assets.push({ key, value }); } }); - removeExpiredBidsFromNativeMapper(); return message; } @@ -431,7 +480,7 @@ export function toOrtbNativeRequest(legacyNativeAssets) { }; for (let key in legacyNativeAssets) { // skip conversion for non-asset keys - if (key in NATIVE_KEYS_THAT_ARE_NOT_ASSETS) continue; + if (NATIVE_KEYS_THAT_ARE_NOT_ASSETS.includes(key)) continue; const asset = legacyNativeAssets[key]; let required = 0; @@ -459,7 +508,7 @@ export function toOrtbNativeRequest(legacyNativeAssets) { if (asset.aspect_ratios) { if (!isArray(asset.aspect_ratios)) { logError("image.aspect_ratios was passed, but it's not a an array:", asset.aspect_ratios); - } else if (asset.aspect_ratios.length != 1) { + } else if (!asset.aspect_ratios.length) { logError("image.aspect_ratios was passed, but it's empty:", asset.aspect_ratios); } else { const { min_width: minWidth, min_height: minHeight } = asset.aspect_ratios[0]; @@ -469,12 +518,20 @@ export function toOrtbNativeRequest(legacyNativeAssets) { ortbAsset.img.wmin = minWidth; ortbAsset.img.hmin = minHeight; } + const aspectRatios = asset.aspect_ratios + .filter((ar) => ar.ratio_width && ar.ratio_height) + .map(ratio => `${ratio.ratio_width}:${ratio.ratio_height}`); + if (aspectRatios.length > 0) { + ortbAsset.img.ext = { + aspectratios: aspectRatios + } + } } } // if asset.sizes exist, by OpenRTB spec we should remove wmin and hmin if (asset.sizes) { - if (asset.sizes.length != 2 || !isInteger(asset.sizes[0]) || !isInteger(asset.sizes[1])) { + if (asset.sizes.length !== 2 || !isInteger(asset.sizes[0]) || !isInteger(asset.sizes[1])) { logError('image.sizes was passed, but its value is not an array of integers:', asset.sizes); } else { ortbAsset.img.w = asset.sizes[0]; @@ -492,9 +549,7 @@ export function toOrtbNativeRequest(legacyNativeAssets) { } // all extensions to the native bid request are passed as is } else if (key === 'ext') { - ortbAsset.ext = { - asset - }; + ortbAsset.ext = asset; // in `ext` case, required field is not needed delete ortbAsset.required; } @@ -572,30 +627,21 @@ export function fromOrtbNativeRequest(openRTBRequest) { * @returns an array of valid bid requests where the openRTB bids are converted to proprietary format. */ export function convertOrtbRequestToProprietaryNative(bidRequests) { - let needsToBeCopied = false; - if (!bidRequests || !isArray(bidRequests)) return bidRequests; if (FEATURES.NATIVE) { + if (!bidRequests || !isArray(bidRequests)) return bidRequests; // check if a conversion is needed - needsToBeCopied = bidRequests.some(bidRequest => bidRequest.mediaTypes && bidRequest.mediaTypes[NATIVE] && bidRequest.mediaTypes[NATIVE].ortb); - if (!needsToBeCopied) return bidRequests; + if (!bidRequests.some(bidRequest => (bidRequest?.mediaTypes || {})[NATIVE]?.ortb)) { + return bidRequests; + } let bidRequestsCopy = deepClone(bidRequests); // convert Native ORTB definition to old-style prebid native definition for (const bidRequest of bidRequestsCopy) { if (bidRequest.mediaTypes && bidRequest.mediaTypes[NATIVE] && bidRequest.mediaTypes[NATIVE].ortb) { - bidRequest.mediaTypes[NATIVE] = { - // to keep other keywords like sendTargetingKeys, rendererUrl... - ...Object.keys(bidRequest.mediaTypes[NATIVE]) - .filter(key => NATIVE_KEYS_THAT_ARE_NOT_ASSETS.includes(key)) - .reduce((obj, key) => ({ - ...obj, - [key]: bidRequest.mediaTypes[NATIVE][key] - }), {}), - ...fromOrtbNativeRequest(bidRequest.mediaTypes[NATIVE].ortb) - } - bidRequest.nativeParams = bidRequest.mediaTypes[NATIVE]; - if (bidRequest.nativeParams) { - processNativeAdUnitParams(bidRequest.nativeParams); - } + bidRequest.mediaTypes[NATIVE] = Object.assign( + pick(bidRequest.mediaTypes[NATIVE], NATIVE_KEYS_THAT_ARE_NOT_ASSETS), + fromOrtbNativeRequest(bidRequest.mediaTypes[NATIVE].ortb) + ); + bidRequest.nativeParams = processNativeAdUnitParams(bidRequest.mediaTypes[NATIVE]); } } return bidRequestsCopy; @@ -603,11 +649,52 @@ export function convertOrtbRequestToProprietaryNative(bidRequests) { return bidRequests; } -export function toOrtbNativeResponse(legacyResponse, ortbRequest) { - const ortbResponse = { +/** + * convert PBJS proprietary native properties that are *not* assets to the ORTB native format. + * + * @param legacyNative `bidResponse.native` object as returned by adapters + */ +export function legacyPropertiesToOrtbNative(legacyNative) { + const response = { link: {}, - assets: [], eventtrackers: [] + } + Object.entries(legacyNative).forEach(([key, value]) => { + switch (key) { + case 'clickUrl': + response.link.url = value; + break; + case 'clickTrackers': + response.link.clicktrackers = Array.isArray(value) ? value : [value]; + break; + case 'impressionTrackers': + (Array.isArray(value) ? value : [value]).forEach(url => { + response.eventtrackers.push({ + event: TRACKER_EVENTS.impression, + method: TRACKER_METHODS.img, + url + }); + }); + break; + case 'javascriptTrackers': + // jstracker is deprecated, but we need to use it here since 'javascriptTrackers' is markup, not an url + // TODO: at the time of writing this, core expected javascriptTrackers to be a string (despite the name), + // but many adapters are passing an array. It's possible that some of them are, in fact, passing URLs and not markup + // in general, native trackers seem to be neglected and/or broken + response.jstracker = Array.isArray(value) ? value.join('') : value; + break; + } + }) + return response; +} + +export function toOrtbNativeResponse(legacyResponse, ortbRequest) { + // copy the request, so we don't pollute it with response data below + ortbRequest = deepClone(ortbRequest); + + const ortbResponse = { + ...legacyPropertiesToOrtbNative(legacyResponse), + assets: [] }; Object.keys(legacyResponse).filter(key => !!legacyResponse[key]).forEach(key => { const value = legacyResponse[key]; @@ -629,20 +716,6 @@ export function toOrtbNativeResponse(legacyResponse, ortbRequest) { }; ortbResponse.assets.push(imageAsset); break; - case 'clickUrl': - ortbResponse.link.url = value; - break; - case 'clickTrackers': - ortbResponse.link.clicktrackers = value; - break; - case 'impressionTrackers': - case 'javascriptTrackers': - ortbResponse.eventtrackers.push({ - event: 1, - method: key == 'impressionTrackers' ? 1 : 2, - url: value, - }); - break; default: if (key in PREBID_NATIVE_DATA_KEYS_TO_ORTB) { const dataAsset = ortbRequest.assets.find(asset => asset.data != null && asset.data.type === NATIVE_ASSET_TYPES[PREBID_NATIVE_DATA_KEYS_TO_ORTB[key]]); @@ -680,38 +753,6 @@ function toLegacyResponse(ortbResponse, ortbRequest) { return legacyResponse; } -/** - * Converts a Legacy native request to OpenRTB. - * The proprietary Prebid format has many limitations and will be dropped in - * the future; adapters are encouraged to stop using it in favour of OpenRTB format. - * @param {BidRequest[]} bidRequests an array of valid bid requests - * @returns an array of valid bid requests where the legacy format is converted to OpenRTB. - */ -export function convertLegacyNativeRequestToOrtb(bidRequests) { - if (!bidRequests || !isArray(bidRequests)) return bidRequests; - // convert Native ORTB definition to old-style prebid native definition - for (const bidRequest of bidRequests) { - if (bidRequest.mediaTypes && bidRequest.mediaTypes[NATIVE]) { - if (bidRequest.mediaTypes[NATIVE].ortb) continue; - // legacy case - const ortbRequest = toOrtbNativeRequest(bidRequest.mediaTypes[NATIVE]); - bidRequest.mediaTypes[NATIVE] = { - ...Object.entries(bidRequest.mediaTypes['native']) - .filter(([key, value]) => NATIVE_KEYS_THAT_ARE_NOT_ASSETS.includes(key)) - .reduce((acc, curr) => { acc[curr[0]] = curr[1]; return acc }, {}), - ortb: ortbRequest - } - nativeMapper.set(bidRequest.bidId, ortbRequest); - // to keep other keywords like sendTargetingKeys, rendererUrl... - bidRequest.nativeParams = bidRequest.mediaTypes[NATIVE]; - if (bidRequest.nativeParams) { - processNativeAdUnitParams(bidRequest.nativeParams); - } - } - } - return bidRequests; -} - /** * Inverts key-values of an object. */ @@ -722,11 +763,3 @@ function inverse(obj) { } return retobj; } - -// to avoid memory leaks, this function will try to remove expired bids from the native wrapper. -function removeExpiredBidsFromNativeMapper() { - const expiredBids = auctionManager.getBidsReceived().filter((bid) => !filters.isBidNotExpired(bid)); - for (const expiredBid of expiredBids) { - nativeMapper.delete(expiredBid.requestId); - } -} diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 0495c7000d1..ff600d4ef93 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1316,55 +1316,54 @@ describe('S2S Adapter', function () { config.setConfig(_config); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.imp[0].native).to.deep.equal({ - request: JSON.stringify({ - 'context': 1, - 'plcmttype': 1, - 'eventtrackers': [{ - event: 1, - methods: [1] - }], - 'assets': [ - { - 'required': 1, - 'id': 0, - 'title': { - 'len': 800 - } - }, - { - 'required': 1, - 'id': 1, - 'img': { - 'type': 3, - 'w': 989, - 'h': 742 - } - }, - { - 'required': 1, - 'id': 2, - 'img': { - 'type': 1, - 'wmin': 10, - 'hmin': 10, - 'ext': { - 'aspectratios': ['1:1'] - } - } - }, - { - 'required': 1, - 'id': 3, - 'data': { - 'type': 1 + const ortbReq = JSON.parse(requestBid.imp[0].native.request); + expect(ortbReq).to.deep.equal({ + 'ver': '1.2', + 'context': 1, + 'plcmttype': 1, + 'eventtrackers': [{ + event: 1, + methods: [1] + }], + 'assets': [ + { + 'required': 1, + 'id': 0, + 'title': { + 'len': 800 + } + }, + { + 'required': 1, + 'id': 1, + 'img': { + 'type': 3, + 'w': 989, + 'h': 742 + } + }, + { + 'required': 1, + 'id': 2, + 'img': { + 'type': 1, + 'wmin': 10, + 'hmin': 10, + 'ext': { + 'aspectratios': ['1:1'] } } - ] - }), - ver: '1.2' + }, + { + 'required': 1, + 'id': 3, + 'data': { + 'type': 1 + } + } + ] }); + expect(requestBid.imp[0].native.ver).to.equal('1.2'); }); it('adds native ortb request for OpenRTB', function () { @@ -1382,11 +1381,9 @@ describe('S2S Adapter', function () { config.setConfig(_config); adapter.callBids(openRtbNativeRequest, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.imp[0].native).to.deep.equal({ - request: JSON.stringify(NATIVE_ORTB_MTO.ortb), - ver: '1.2' - }); + const nativeReq = JSON.parse(requestBid.imp[0].native.request); + expect(nativeReq).to.deep.equal(NATIVE_ORTB_MTO.ortb); + expect(requestBid.imp[0].native.ver).to.equal('1.2'); }); it('should not include ext.aspectratios if adunit\'s aspect_ratios do not define radio_width and ratio_height', () => { diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index ce69c299697..a6f8e7ae891 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -8,7 +8,7 @@ import { decorateAdUnitsWithNativeParams, isOpenRTBBidRequestValid, isNativeOpenRTBBidValid, - toOrtbNativeRequest, + toOrtbNativeRequest, toOrtbNativeResponse, legacyPropertiesToOrtbNative, fireImpressionTrackers, fireClickTrackers, } from 'src/native.js'; import CONSTANTS from 'src/constants.json'; import { stubAuctionIndex } from '../helpers/indexStub.js'; @@ -332,7 +332,7 @@ describe('native.js', function () { adId: '123', }; - const message = getAllAssetsMessage(messageRequest, bid); + const message = getAllAssetsMessage(messageRequest, bid, {getNativeReq: () => null}); expect(message.assets.length).to.equal(9); expect(message.assets).to.deep.include({ @@ -380,7 +380,7 @@ describe('native.js', function () { adId: '123', }; - const message = getAllAssetsMessage(messageRequest, bidWithUndefinedFields); + const message = getAllAssetsMessage(messageRequest, bidWithUndefinedFields, {getNativeReq: () => null}); expect(message.assets.length).to.equal(4); expect(message.assets).to.deep.include({ @@ -883,3 +883,142 @@ describe('validate native', function () { }); } }); + +describe('legacyPropertiesToOrtbNative', () => { + describe('click trakckers', () => { + it('should convert clickUrl to link.url', () => { + const native = legacyPropertiesToOrtbNative({clickUrl: 'some-url'}); + expect(native.link.url).to.eql('some-url'); + }); + it('should convert single clickTrackers to link.clicktrackers', () => { + const native = legacyPropertiesToOrtbNative({clickTrackers: 'some-url'}); + expect(native.link.clicktrackers).to.eql([ + 'some-url' + ]) + }); + it('should convert multiple clickTrackers into link.clicktrackers', () => { + const native = legacyPropertiesToOrtbNative({clickTrackers: ['url1', 'url2']}); + expect(native.link.clicktrackers).to.eql([ + 'url1', + 'url2' + ]) + }) + }); + describe('impressionTrackers', () => { + it('should convert a single tracker into an eventtracker entry', () => { + const native = legacyPropertiesToOrtbNative({impressionTrackers: 'some-url'}); + expect(native.eventtrackers).to.eql([ + { + event: 1, + method: 1, + url: 'some-url' + } + ]); + }); + + it('should convert an array into corresponding eventtracker entries', () => { + const native = legacyPropertiesToOrtbNative({impressionTrackers: ['url1', 'url2']}); + expect(native.eventtrackers).to.eql([ + { + event: 1, + method: 1, + url: 'url1' + }, + { + event: 1, + method: 1, + url: 'url2' + } + ]) + }) + }); + describe('javascriptTrackers', () => { + it('should convert a single value into jstracker', () => { + const native = legacyPropertiesToOrtbNative({javascriptTrackers: 'some-markup'}); + expect(native.jstracker).to.eql('some-markup'); + }) + it('should merge multiple values into a single jstracker', () => { + const native = legacyPropertiesToOrtbNative({javascriptTrackers: ['some-markup', 'some-other-markup']}); + expect(native.jstracker).to.eql('some-markupsome-other-markup'); + }) + }); +}); + +describe('fireImpressionTrackers', () => { + let runMarkup, fetchURL; + beforeEach(() => { + runMarkup = sinon.stub(); + fetchURL = sinon.stub(); + }) + + function runTrackers(resp) { + fireImpressionTrackers(resp, {runMarkup, fetchURL}) + } + + it('should run markup in jstracker', () => { + runTrackers({ + jstracker: 'some-markup' + }); + sinon.assert.calledWith(runMarkup, 'some-markup'); + }); + + it('should fetch each url in imptrackers', () => { + const urls = ['url1', 'url2']; + runTrackers({ + imptrackers: urls + }); + urls.forEach(url => sinon.assert.calledWith(fetchURL, url)); + }); + + it('should fetch each url in eventtrackers that use the image method', () => { + const urls = ['url1', 'url2']; + runTrackers({ + eventtrackers: urls.map(url => ({event: 1, method: 1, url})) + }); + urls.forEach(url => sinon.assert.calledWith(fetchURL, url)) + }); + + it('should load as a script each url in eventtrackers that use the js method', () => { + const urls = ['url1', 'url2']; + runTrackers({ + eventtrackers: urls.map(url => ({event: 1, method: 2, url})) + }); + urls.forEach(url => sinon.assert.calledWith(runMarkup, sinon.match(`script async src="${url}"`))) + }); + + it('should not fire trackers that are not impression trakcers', () => { + runTrackers({ + link: { + clicktrackers: ['click-url'] + }, + eventtrackers: [{ + event: 2, // not imp + method: 1, + url: 'some-url' + }] + }); + sinon.assert.notCalled(fetchURL); + sinon.assert.notCalled(runMarkup); + }) +}) + +describe('fireClickTrackers', () => { + let fetchURL; + beforeEach(() => { + fetchURL = sinon.stub(); + }); + + function runTrackers(resp) { + fireClickTrackers(resp, {fetchURL}); + } + + it('should load each URL in link.clicktrackers', () => { + const urls = ['url1', 'url2']; + runTrackers({ + link: { + clicktrackers: urls + } + }); + urls.forEach(url => sinon.assert.calledWith(fetchURL, url)); + }) +}) diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 646791c7e1f..c040bba4eec 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -12,6 +12,7 @@ import {hook} from '../../../../src/hook.js'; import {auctionManager} from '../../../../src/auctionManager.js'; import {stubAuctionIndex} from '../../../helpers/indexStub.js'; import { bidderSettings } from '../../../../src/bidderSettings.js'; +import {decorateAdUnitsWithNativeParams} from '../../../../src/native.js'; const CODE = 'sampleBidder'; const MOCK_BIDS_REQUEST = { @@ -882,6 +883,7 @@ describe('validate bid response: ', function () { title: {'required': true}, } }] + decorateAdUnitsWithNativeParams(adUnits); let bidRequest = { bids: [{ bidId: '1', @@ -923,6 +925,7 @@ describe('validate bid response: ', function () { title: {'required': true}, }, }]; + decorateAdUnitsWithNativeParams(adUnits); let bidRequest = { bids: [{ bidId: '1', From d0a729b04237d8106b6d1afb400c2274fe0c9ade Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 28 Jul 2022 17:49:13 +0000 Subject: [PATCH 091/569] Prebid 7.8.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef3ab0394fb..bbd66c24b7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.8.0-pre", + "version": "7.8.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9a2e223fe9d..4d737ec389c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.8.0-pre", + "version": "7.8.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From ef86ab1d6a1bbc2fdbfc2dcccffef74d1349768f Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 28 Jul 2022 17:49:14 +0000 Subject: [PATCH 092/569] Increment version to 7.9.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bbd66c24b7f..c29ddad3673 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.8.0", + "version": "7.9.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 4d737ec389c..3c6622adf9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.8.0", + "version": "7.9.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 82d74d2f57ceac5511fdcfd8d35a433f07d3a2ca Mon Sep 17 00:00:00 2001 From: nllerandi3lift <75995508+nllerandi3lift@users.noreply.github.com> Date: Thu, 28 Jul 2022 18:44:10 -0400 Subject: [PATCH 093/569] Triplelift Bid Adapter: outstream support (#8709) * alt outstream support * removes consoleLog * rename video ttl * multi-imp * simplify video if statement in buildPostBody * distinguish banner from video bid; temp solution for testing * adds tests for mediatype * remove console log * individual instream/outstream tests * simplify some functions * nitpick * adds media_type check * adds placement options to outstream * checks instream placement values * TL-19850 Finished log error logic around floors functionality * deprecates getlegacyFpd * remove console log * TL-19850 Changed functionlity of tests * restore logErrorSpy aftereach * removing pub name from test Co-authored-by: Dan Goldin Co-authored-by: Patrick Loughrey Co-authored-by: Patrick Loughrey --- modules/tripleliftBidAdapter.js | 64 ++- .../spec/modules/tripleliftBidAdapter_spec.js | 388 +++++++++++++++++- 2 files changed, 423 insertions(+), 29 deletions(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index df4f9a9ba38..a5a93024ff9 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -8,7 +8,7 @@ const GVLID = 28; const BIDDER_CODE = 'triplelift'; const STR_ENDPOINT = 'https://tlx.3lift.com/header/auction?'; const BANNER_TIME_TO_LIVE = 300; -const INSTREAM_TIME_TO_LIVE = 3600; +const VIDEO_TIME_TO_LIVE = 3600; let gdprApplies = true; let consentString = null; export const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); @@ -120,12 +120,15 @@ function _buildPostBody(bidRequests, bidderRequest) { tagid: bidRequest.params.inventoryCode, floor: _getFloor(bidRequest) }; - // remove the else to support multi-imp - if (_isInstreamBidRequest(bidRequest)) { + // Check for video bidrequest + if (_isVideoBidRequest(bidRequest)) { imp.video = _getORTBVideo(bidRequest); - } else if (bidRequest.mediaTypes.banner) { + } + // append banner if applicable and request is not for instream + if (bidRequest.mediaTypes.banner && !_isInstream(bidRequest)) { imp.banner = { format: _sizes(bidRequest.sizes) }; - }; + } + if (!isEmpty(bidRequest.ortb2Imp)) { imp.fpd = _getAdUnitFpd(bidRequest.ortb2Imp); } @@ -153,22 +156,41 @@ function _buildPostBody(bidRequests, bidderRequest) { return data; } -function _isInstreamBidRequest(bidRequest) { - if (!bidRequest.mediaTypes.video) return false; - if (!bidRequest.mediaTypes.video.context) return false; - if (bidRequest.mediaTypes.video.context.toLowerCase() === 'instream') { - return true; - } else { - return false; - } +function _isVideoBidRequest(bidRequest) { + return _isValidVideoObject(bidRequest) && (_isInstream(bidRequest) || _isOutstream(bidRequest)); +} + +function _isOutstream(bidRequest) { + return _isValidVideoObject(bidRequest) && bidRequest.mediaTypes.video.context.toLowerCase() === 'outstream'; +} + +function _isInstream(bidRequest) { + return _isValidVideoObject(bidRequest) && bidRequest.mediaTypes.video.context.toLowerCase() === 'instream'; +} + +function _isValidVideoObject(bidRequest) { + return bidRequest.mediaTypes.video && bidRequest.mediaTypes.video.context; } function _getORTBVideo(bidRequest) { // give precedent to mediaTypes.video let video = { ...bidRequest.params.video, ...bidRequest.mediaTypes.video }; - if (!video.w) video.w = video.playerSize[0][0]; - if (!video.h) video.h = video.playerSize[0][1]; + try { + if (!video.w) video.w = video.playerSize[0][0]; + if (!video.h) video.h = video.playerSize[0][1]; + } catch (err) { + logWarn('Video size not defined', err); + } if (video.context === 'instream') video.placement = 1; + if (video.context === 'outstream') { + if (!video.placement) { + video.placement = 3 + } else if ([3, 4, 5].indexOf(video.placement) === -1) { + logMessage(`video.placement value of ${video.placement} is invalid for outstream context. Setting placement to 3`) + video.placement = 3 + } + } + // clean up oRTB object delete video.playerSize; return video; @@ -180,7 +202,7 @@ function _getFloor (bid) { try { const floorInfo = bid.getFloor({ currency: 'USD', - mediaType: _isInstreamBidRequest(bid) ? 'video' : 'banner', + mediaType: _isVideoBidRequest(bid) ? 'video' : 'banner', size: '*' }); if (typeof floorInfo === 'object' && @@ -366,10 +388,10 @@ function _buildResponseObject(bidderRequest, bid) { meta: {} }; - if (_isInstreamBidRequest(breq)) { + if (_isVideoBidRequest(breq) && bid.media_type === 'video') { bidResponse.vastXml = bid.ad; bidResponse.mediaType = 'video'; - bidResponse.ttl = INSTREAM_TIME_TO_LIVE; + bidResponse.ttl = VIDEO_TIME_TO_LIVE; }; if (bid.advertiser_name) { @@ -381,7 +403,11 @@ function _buildResponseObject(bidderRequest, bid) { } if (bid.tl_source && bid.tl_source == 'hdx') { - bidResponse.meta.mediaType = 'banner'; + if (_isVideoBidRequest(breq) && bid.media_type === 'video') { + bidResponse.meta.mediaType = 'video' + } else { + bidResponse.meta.mediaType = 'banner' + } } if (bid.tl_source && bid.tl_source == 'tlx') { diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index b0269aaf077..bfcfce1ccb4 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -345,6 +345,209 @@ describe('triplelift adapter', function () { auctionId: '1d1a030790a475', userId: {}, schain, + }, + // outstream video only + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480] + } + }, + adUnitCode: 'adunit-code-outstream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // banner and incomplete outstream (missing size) + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6 + } + }, + mediaTypes: { + video: { + context: 'outstream' + }, + banner: { + sizes: [ + [970, 250], + [1, 1] + ] + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; valid placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + placement: 3 + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; valid placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + placement: 4 + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; valid placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + placement: 5 + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; undefined placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480] + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; invalid placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + placement: 6 + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, } ]; @@ -415,13 +618,16 @@ describe('triplelift adapter', function () { expect(payload.imp[0].tagid).to.equal('12345'); expect(payload.imp[0].floor).to.equal(1.0); expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + // instream expect(payload.imp[1].tagid).to.equal('insteam_test'); expect(payload.imp[1].floor).to.equal(1.0); expect(payload.imp[1].video).to.exist.and.to.be.a('object'); + expect(payload.imp[1].video.placement).to.equal(1); // banner and outstream video - expect(payload.imp[2]).to.not.have.property('video'); + expect(payload.imp[2]).to.have.property('video'); expect(payload.imp[2]).to.have.property('banner'); expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(payload.imp[2].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream', 'placement': 3}); // banner and incomplete video expect(payload.imp[3]).to.not.have.property('video'); expect(payload.imp[3]).to.have.property('banner'); @@ -434,10 +640,51 @@ describe('triplelift adapter', function () { expect(payload.imp[5]).to.not.have.property('banner'); expect(payload.imp[5]).to.have.property('video'); expect(payload.imp[5].video).to.exist.and.to.be.a('object'); + expect(payload.imp[5].video.placement).to.equal(1); // banner and outream video and native - expect(payload.imp[6]).to.not.have.property('video'); + expect(payload.imp[6]).to.have.property('video'); expect(payload.imp[6]).to.have.property('banner'); expect(payload.imp[6].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(payload.imp[6].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream', 'placement': 3}); + // outstream video only + expect(payload.imp[7]).to.have.property('video'); + expect(payload.imp[7]).to.not.have.property('banner'); + expect(payload.imp[7].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream', 'placement': 3}); + // banner and incomplete outstream (missing size); video request is permitted so banner can still monetize + expect(payload.imp[8]).to.have.property('video'); + expect(payload.imp[8]).to.have.property('banner'); + expect(payload.imp[8].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(payload.imp[8].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'context': 'outstream', 'placement': 3}); + }); + + it('should check for valid outstream placement values', function () { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const payload = request.data; + // outstream video; valid placement + expect(payload.imp[9]).to.not.have.property('banner'); + expect(payload.imp[9]).to.have.property('video'); + expect(payload.imp[9].video).to.exist.and.to.be.a('object'); + expect(payload.imp[9].video.placement).to.equal(3); + // outstream video; valid placement + expect(payload.imp[10]).to.not.have.property('banner'); + expect(payload.imp[10]).to.have.property('video'); + expect(payload.imp[10].video).to.exist.and.to.be.a('object'); + expect(payload.imp[10].video.placement).to.equal(4); + // outstream video; valid placement + expect(payload.imp[11]).to.not.have.property('banner'); + expect(payload.imp[11]).to.have.property('video'); + expect(payload.imp[11].video).to.exist.and.to.be.a('object'); + expect(payload.imp[11].video.placement).to.equal(5); + // outstream video; undefined placement + expect(payload.imp[12]).to.not.have.property('banner'); + expect(payload.imp[12]).to.have.property('video'); + expect(payload.imp[12].video).to.exist.and.to.be.a('object'); + expect(payload.imp[12].video.placement).to.equal(3); + // outstream video; invalid placement + expect(payload.imp[13]).to.not.have.property('banner'); + expect(payload.imp[13]).to.have.property('video'); + expect(payload.imp[13].video).to.exist.and.to.be.a('object'); + expect(payload.imp[13].video.placement).to.equal(3); }); it('should add tdid to the payload if included', function () { @@ -913,14 +1160,40 @@ describe('triplelift adapter', function () { iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', tl_source: 'tlx', advertiser_name: 'fake advertiser name', - adomain: ['basspro.com', 'internetalerts.org'] + adomain: ['basspro.com', 'internetalerts.org'], + media_type: 'banner' }, { imp_id: 1, crid: '10092_76480_i2j6qm8u', cpm: 9.99, - ad: 'The Trade Desk', - tlx_source: 'hdx' + ad: 'The Trade Desk', + tl_source: 'hdx', + media_type: 'video' + }, + // video bid on banner+outstream request + { + imp_id: 2, + crid: '5989_33264_352817187', + cpm: 20, + ad: '\n \n \t', + tl_source: 'hdx', + advertiser_name: 'zennioptical.com', + adomain: ['zennioptical.com'], + media_type: 'video' + }, + // banner bid on banner+outstream request + { + imp_id: 3, + crid: '5989_33264_352817187', + cpm: 20, + width: 970, + height: 250, + ad: 'ad-markup', + tl_source: 'hdx', + advertiser_name: 'zennioptical.com', + adomain: ['zennioptical.com'], + media_type: 'banner' } ] } @@ -946,13 +1219,13 @@ describe('triplelift adapter', function () { ] } }, - bidId: '30b31c1838de1e', + bidId: '30b31c1838de1e' }, { imp_id: 1, crid: '10092_76480_i2j6qm8u', cpm: 9.99, - ad: 'The Trade Desk', + ad: 'The Trade Desk', tlx_source: 'hdx', mediaTypes: { video: { @@ -960,7 +1233,89 @@ describe('triplelift adapter', function () { playerSize: [640, 480] } }, - bidId: '30b31c1838de1e', + bidId: '30b31c1838de1e' + }, + // banner and outstream + { + bidder: 'triplelift', + params: { + inventoryCode: 'testing_desktop_outstream', + floor: 1 + }, + nativeParams: {}, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1 + }, + banner: { + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ] + }, + native: {} + }, + adUnitCode: 'video-outstream', + transactionId: '135061c3-f546-4e28-8a07-44c2fb58a958', + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ], + bidId: '73edc0ba8de203', + bidderRequestId: '3d81143328560b', + auctionId: 'f6427dc0-b954-4010-a76c-d498380796a2', + src: 'client', + bidRequestsCount: 2, + bidderRequestsCount: 2, + bidderWinsCount: 0 + }, + // banner and outstream + { + bidder: 'triplelift', + params: { + inventoryCode: 'testing_desktop_outstream', + floor: 1 + }, + nativeParams: {}, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1 + }, + banner: { + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ] + }, + native: {} + }, + adUnitCode: 'video-outstream', + transactionId: '135061c3-f546-4e28-8a07-44c2fb58a958', + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ], + bidId: '73edc0ba8de203', + bidderRequestId: '3d81143328560b', + auctionId: 'f6427dc0-b954-4010-a76c-d498380796a2', + src: 'client', + bidRequestsCount: 2, + bidderRequestsCount: 2, + bidderWinsCount: 0 } ], refererInfo: { @@ -1007,16 +1362,29 @@ describe('triplelift adapter', function () { } ]; let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); - expect(result).to.have.length(2); + expect(result).to.have.length(4); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); expect(Object.keys(result[1])).to.have.members(Object.keys(expectedResponse[1])); expect(result[0].ttl).to.equal(300); expect(result[1].ttl).to.equal(3600); }); + it('should identify format of bid and respond accordingly', function() { + let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + expect(result[0].meta.mediaType).to.equal('native'); + expect(result[1].mediaType).to.equal('video'); + expect(result[1].meta.mediaType).to.equal('video'); + // video bid on banner+outstream request + expect(result[2].mediaType).to.equal('video'); + expect(result[2].meta.mediaType).to.equal('video'); + expect(result[2].vastXml).to.include('aid=148508128401385324170&inv_code=testing_mobile_outstream'); + // banner bid on banner+outstream request + expect(result[3].meta.mediaType).to.equal('banner'); + }) + it('should return multiple responses to support SRA', function () { let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); - expect(result).to.have.length(2); + expect(result).to.have.length(4); }); it('should include the advertiser name in the meta field if available', function () { From a231b0da5935b474f5341a9070f7951222f2aeed Mon Sep 17 00:00:00 2001 From: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Date: Mon, 1 Aug 2022 18:40:29 +0200 Subject: [PATCH 094/569] Improve Digital adapter: support for JS trackers in native 1.2 (#8701) --- modules/improvedigitalBidAdapter.js | 3 +++ test/spec/modules/improvedigitalBidAdapter_spec.js | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index dd2f85dcff5..a362f85654c 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -434,6 +434,9 @@ const ID_REQUEST = { return null; } const request = { + eventtrackers: [ + {event: 1, methods: [1, 2]} + ], assets: [], } for (let i of Object.keys(nativeParams)) { diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index b44859f73a0..a882f5ca5ef 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -248,7 +248,7 @@ describe('Improve Digital Adapter Tests', function () { { id: '33e9500b21129f', native: { - request: '{"assets":[{"id":3,"required":1,"data":{"type":2}}]}', + request: '{"eventtrackers":[{"event":1,"methods":[1,2]}],"assets":[{"id":3,"required":1,"data":{"type":2}}]}', ver: '1.2' }, secure: 0, @@ -277,7 +277,7 @@ describe('Improve Digital Adapter Tests', function () { const payload = JSON.parse(spec.buildRequests([nativeBidRequest], {})[0].data); expect(payload.imp[0].native).to.deep.equal({ ver: '1.2', - request: '{\"assets\":[{\"id\":0,\"required\":1,\"title\":{\"len\":140}},{\"id\":3,\"required\":1,\"data\":{\"type\":2}}]}' + request: '{"eventtrackers":[{"event":1,"methods":[1,2]}],"assets":[{"id":0,"required":1,"title":{"len":140}},{"id":3,"required":1,"data":{"type":2}}]}' }); }); @@ -299,7 +299,7 @@ describe('Improve Digital Adapter Tests', function () { const payload = JSON.parse(spec.buildRequests([nativeBidRequest], {})[0].data); expect(payload.imp[0].native).to.deep.equal({ ver: '1.2', - request: '{\"assets\":[{\"id\":0,\"required\":1,\"title\":{\"len\":140}},{\"id\":3,\"required\":1,\"data\":{\"type\":2}}]}' + request: '{"eventtrackers":[{"event":1,"methods":[1,2]}],"assets":[{"id":0,"required":1,"title":{"len":140}},{"id":3,"required":1,"data":{"type":2}}]}' }); }); @@ -930,7 +930,7 @@ describe('Improve Digital Adapter Tests', function () { 'exp': 120, 'id': '52098fad-20c1-476b-a4fa-41e275e5a4a5', 'price': 1.8600000000000003, - 'adm': "{\"ver\":\"1.1\",\"imptrackers\":[\"https://secure.adnxs.com/imptr?id=52311&t=2\",\"https://euw-ice.360yield.com/imp_pixel?ic=hcUBlCANx1FabHBf6FR2gC7UO4xEyXahdZAn0-B5qL-bb3A74BJ1smyWIyW7IWcC0SOjSXzVpevTHXxTqJ.sf.Qhahyy6tSo.0j1QWfXlH8sM4-8vKWjMjw-x.IrJJNlwkQ0s1CdwcwTefcLXm5l2E-W19VhACuV7f3mgrZMNjiSw.SjJAfyPC3SIyAMRjYfj53UmjriQ46T7lhmkqxK8wHmksYCdbZc3PZESk8NWl28sxdjNvnYYCFMcJbeav.LOLabyTXfwy-1cEPbQs.IKMRZIKaqccTDPV3wOtzbNv0jQzatd3Nnv-PGFQcjQ-GW3i27W04Fws4kodpFSn-B6VwZAjzLzoyd5gBncyRnAyCplEbgHU5sZ1IyKHWjgCl3ZtRIK5vqrRD5D-xqgSnOi7-phG.CqZWDZ4bMDSfQg2ZnbvUTyGKcEl0WR59dW5izTMV4Fjizcrvr5T-t.zMbGwz.hGnmLIyhTqh.IcwW.GiDLVExlDlix5S1LXIWVsSyrQ==\"],\"assets\":[{\"id\":1,\"data\":{\"value\":\"ImproveDigital\",\"type\":1}},{\"id\":3,\"data\":{\"value\":\"Test content.\",\"type\":2}},{\"id\":0,\"title\":{\"text\":\"Sample Prebid Test Title\"}}],\"link\":{\"url\":\"https://euw-ice.360yield.com/click/hcUBlHOV7YhVse8RyBa0ajjyPa9Vt17e4g-1m3cRj3E67vq-RYux.SiUeAmBfNBcoOqkUc6A15AWmi4yFu5K-BdkaYjildyyk7fNLyR6hWr411kv4vrFwm5jrIBceuHS6K8oN69f.uCo8zGTdR2TbSlldwcpahQPlufZU.6VaMsu4IC53uEiUT5vb7kAw6TTlxuGBNq6zaGryiWEV2.N3YYJDTyYPh8tv-ZFyeFZFm0Gnjv.xWbC.70JcRUVU9UelQaPsTpTWYTXBhJt84YJUw1-GNtaLNVLSjjZbVoA2fsMti5p6OBmF.7u39on2OPgvseIkSmge7Pqg63pRqdP75hp.DAEk6OkcN1jGnwP2DSbvpaSbin5lVqjfO0B-wnQgfQTCUtM5v4JmkNweLhUf9Q-x.nPKLW5SccEk9ZFXzY2-1wpT3PWm8Tix3NRscLPZub9wHzL..pl6ip8cQ9hp16UjwT4H6RMAxL0R7bl-h2pAicGAzYmuO7ntRESKUoIWA==//http%3A%2F%2Fquantum-advertising.com%2Ffr%2F\"},\"jstracker\":\"\"}", + 'adm': '{"ver":"1.1","imptrackers":["https://secure.adnxs.com/imptr?id=52311&t=2","https://euw-ice.360yield.com/imp_pixel?ic=hcUBlCANx1FabHBf6FR2gC7UO4xEyXahdZAn0-B5qL-bb3A74BJ1smyWIyW7IWcC0SOjSXzVpevTHXxTqJ.sf.Qhahyy6tSo.0j1QWfXlH8sM4-8vKWjMjw-x.IrJJNlwkQ0s1CdwcwTefcLXm5l2E-W19VhACuV7f3mgrZMNjiSw.SjJAfyPC3SIyAMRjYfj53UmjriQ46T7lhmkqxK8wHmksYCdbZc3PZESk8NWl28sxdjNvnYYCFMcJbeav.LOLabyTXfwy-1cEPbQs.IKMRZIKaqccTDPV3wOtzbNv0jQzatd3Nnv-PGFQcjQ-GW3i27W04Fws4kodpFSn-B6VwZAjzLzoyd5gBncyRnAyCplEbgHU5sZ1IyKHWjgCl3ZtRIK5vqrRD5D-xqgSnOi7-phG.CqZWDZ4bMDSfQg2ZnbvUTyGKcEl0WR59dW5izTMV4Fjizcrvr5T-t.zMbGwz.hGnmLIyhTqh.IcwW.GiDLVExlDlix5S1LXIWVsSyrQ=="],"assets":[{"id":1,"data":{"value":"ImproveDigital","type":1}},{"id":3,"data":{"value":"Test content.","type":2}},{"id":0,"title":{"text":"Sample Prebid Test Title"}}],"link":{"url":"https://euw-ice.360yield.com/click/hcUBlHOV7YhVse8RyBa0ajjyPa9Vt17e4g-1m3cRj3E67vq-RYux.SiUeAmBfNBcoOqkUc6A15AWmi4yFu5K-BdkaYjildyyk7fNLyR6hWr411kv4vrFwm5jrIBceuHS6K8oN69f.uCo8zGTdR2TbSlldwcpahQPlufZU.6VaMsu4IC53uEiUT5vb7kAw6TTlxuGBNq6zaGryiWEV2.N3YYJDTyYPh8tv-ZFyeFZFm0Gnjv.xWbC.70JcRUVU9UelQaPsTpTWYTXBhJt84YJUw1-GNtaLNVLSjjZbVoA2fsMti5p6OBmF.7u39on2OPgvseIkSmge7Pqg63pRqdP75hp.DAEk6OkcN1jGnwP2DSbvpaSbin5lVqjfO0B-wnQgfQTCUtM5v4JmkNweLhUf9Q-x.nPKLW5SccEk9ZFXzY2-1wpT3PWm8Tix3NRscLPZub9wHzL..pl6ip8cQ9hp16UjwT4H6RMAxL0R7bl-h2pAicGAzYmuO7ntRESKUoIWA==//http%3A%2F%2Fquantum-advertising.com%2Ffr%2F"},"jstracker":""}', 'impid': '33e9500b21129f', 'cid': '196108' } From d46661a4aa8994d57f3bea3055bd4e4c8aa50e10 Mon Sep 17 00:00:00 2001 From: Sasikumar Dharmaraj <83545471+sasikumar-c1x@users.noreply.github.com> Date: Tue, 2 Aug 2022 20:36:50 +0530 Subject: [PATCH 095/569] c1x Bid Adapter: adding back adapter with updated pbjs compliance (#8706) * C1X Adapter GDPR Support * remove the redundant userSync * fixed keyword spacing issue * c1x adapter changes * c1x adapter latest changes * added deal id support for ssp endpoint * changes for ne bidder parameters in md file * changes * changes * test cases added * Test cases changes * test cases added * test cases changes * test cases changes * changes * changes * changes * changes * changes for log * changes suggested from PR Co-authored-by: Cathy Huang Co-authored-by: sasikumar dharmaraj Co-authored-by: Devyani Choubey Co-authored-by: devyaniChoubey --- modules/c1xBidAdapter.js | 209 ++++++++++++++++++++++++ modules/c1xBidAdapter.md | 70 ++++++++ test/spec/modules/c1xBidAdapter_spec.js | 189 +++++++++++++++++++++ 3 files changed, 468 insertions(+) create mode 100644 modules/c1xBidAdapter.js create mode 100644 modules/c1xBidAdapter.md create mode 100644 test/spec/modules/c1xBidAdapter_spec.js diff --git a/modules/c1xBidAdapter.js b/modules/c1xBidAdapter.js new file mode 100644 index 00000000000..8b579f52299 --- /dev/null +++ b/modules/c1xBidAdapter.js @@ -0,0 +1,209 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { logInfo, logError } from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'c1x'; +const URL = 'https://hb-stg.c1exchange.com/ht'; +// const PIXEL_ENDPOINT = '//px.c1exchange.com/pubpixel/'; +const LOG_MSG = { + invalidBid: 'C1X: [ERROR] bidder returns an invalid bid', + noSite: 'C1X: [ERROR] no site id supplied', + noBid: 'C1X: [INFO] creating a NO bid for Adunit: ', + bidWin: 'C1X: [INFO] creating a bid for Adunit: ' +}; + +/** + * Adapter for requesting bids from C1X header tag server. + * v3.1 (c) C1X Inc., 2018 + */ + +export const c1xAdapter = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + // check the bids sent to c1x bidder + isBidRequestValid: function (bid) { + if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { + return false; + } + if (typeof bid.params.placementId === 'undefined') { + return false; + } + return true; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + let payload = {}; + let tagObj = {}; + let bidRequest = []; + const adunits = validBidRequests.length; + const rnd = new Date().getTime(); + const c1xTags = validBidRequests.map(bidToTag); + const bidIdTags = validBidRequests.map(bidToShortTag); // include only adUnitCode and bidId from request obj + + // flattened tags in a tag object + tagObj = c1xTags.reduce((current, next) => Object.assign(current, next)); + + payload = { + adunits: adunits.toString(), + rnd: rnd.toString(), + response: 'json', + compress: 'gzip' + }; + + // for GDPR support + if (bidderRequest && bidderRequest.gdprConsent) { + payload['consent_string'] = bidderRequest.gdprConsent.consentString; + payload['consent_required'] = (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies.toString() : 'true' + ; + } + + Object.assign(payload, tagObj); + let payloadString = stringifyPayload(payload); + // ServerRequest object + bidRequest.push({ + method: 'GET', + url: URL, + data: payloadString, + bids: bidIdTags + }) + return bidRequest; + }, + + interpretResponse: function (serverResponse, requests) { + serverResponse = serverResponse.body; + requests = requests.bids || []; + const currency = 'USD'; + const bidResponses = []; + let netRevenue = false; + + if (!serverResponse || serverResponse.error) { + let errorMessage = serverResponse.error; + logError(LOG_MSG.invalidBid + errorMessage); + return bidResponses; + } else { + serverResponse.forEach(bid => { + logInfo(bid) + if (bid.bid) { + if (bid.bidType === 'NET_BID') { + netRevenue = !netRevenue; + } + const curBid = { + requestId: bid.bidId, + width: bid.width, + height: bid.height, + cpm: bid.cpm, + ad: bid.ad, + creativeId: bid.crid, + currency: currency, + ttl: 300, + netRevenue: netRevenue + }; + + if (bid.dealId) { + curBid['dealId'] = bid.dealId + } + + for (let i = 0; i < requests.length; i++) { + if (bid.adId === requests[i].adUnitCode) { + curBid.requestId = requests[i].bidId; + } + } + logInfo(LOG_MSG.bidWin + bid.adId + ' size: ' + curBid.width + 'x' + curBid.height); + bidResponses.push(curBid); + } else { + // no bid + logInfo(LOG_MSG.noBid + bid.adId); + } + }); + } + + return bidResponses; + } + +} + +function bidToTag(bid, index) { + const tag = {}; + const adIndex = 'a' + (index + 1).toString(); // ad unit id for c1x + const sizeKey = adIndex + 's'; + const priceKey = adIndex + 'p'; + const dealKey = adIndex + 'd'; + // TODO: Multiple Floor Prices + + const sizesArr = bid.sizes; + const floorPriceMap = getBidFloor(bid); + + const dealId = bid.params.dealId || ''; + + if (dealId) { + tag[dealKey] = dealId; + } + + tag[adIndex] = bid.adUnitCode; + tag[sizeKey] = sizesArr.reduce((prev, current) => prev + (prev === '' ? '' : ',') + current.join('x'), ''); + const newSizeArr = tag[sizeKey].split(','); + if (floorPriceMap) { + newSizeArr.forEach(size => { + if (size in floorPriceMap) { + tag[priceKey] = floorPriceMap[size].toString(); + } // we only accept one cpm price in floorPriceMap + }); + } + if (bid.params.pageurl) { + tag['pageurl'] = bid.params.pageurl; + } + + return tag; +} + +function getBidFloor(bidRequest) { + let floorInfo = {}; + + if (typeof bidRequest.getFloor === 'function') { + floorInfo = bidRequest.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: '*', + }); + } + + let floor = + floorInfo.floor || + bidRequest.params.bidfloor || + bidRequest.params.floorPriceMap || + 0; + + return floor; +} + +function bidToShortTag(bid) { + const tag = {}; + tag.adUnitCode = bid.adUnitCode; + tag.bidId = bid.bidId; + return tag; +} + +function stringifyPayload(payload) { + let payloadString = []; + for (var key in payload) { + if (payload.hasOwnProperty(key)) { + payloadString.push(key + '=' + payload[key]); + } + } + return payloadString.join('&'); +} + +registerBidder(c1xAdapter); diff --git a/modules/c1xBidAdapter.md b/modules/c1xBidAdapter.md new file mode 100644 index 00000000000..9a7cef486d2 --- /dev/null +++ b/modules/c1xBidAdapter.md @@ -0,0 +1,70 @@ +# Overview + +Module Name: C1X Bidder Adapter +Module Type: Bidder Adapter +Maintainer: vishnu@c1exchange.com + +# Description + +Module that connects to C1X's demand sources + +# Test Parameters +``` + var adUnits = [{ + code: 'test-div-1', + mediaTypes: { + banner: { + sizes: [[750, 200]], + } + }, + bids: [{ + bidder: 'c1x', + params: { + placementId: 'div-gpt-ad-1654594619717-0', + 'floorPriceMap': { + '300x250': 4.35 + } + } + }] + }, { + code: 'test-div-2', + mediaTypes: { + banner: { + sizes: [[300, 250], [750, 200]], + } + }, + bids: [{ + bidder: 'c1x', + params: { + placementId: 'div-gpt-ad-1654940683355-0', + 'floorPriceMap': { + '300x250': 4.35 + } + dealId: '1233' // optional parameter + } + }] + }]; + + + pbjs.bidderSettings = { + c1x: { + siteId: 999, + adserverTargeting: [{ + key: "hb_bidder", + val: function(bidResponse) { + return bidResponse.bidderCode; + } + }, { + key: "hb_adid", + val: function(bidResponse) { + return bidResponse.adId; + } + }, { + key: "hb_pb", + val: function(bidResponse) { + return bidResponse.pbLg; + } + }] + } + } +``` \ No newline at end of file diff --git a/test/spec/modules/c1xBidAdapter_spec.js b/test/spec/modules/c1xBidAdapter_spec.js new file mode 100644 index 00000000000..315680cba26 --- /dev/null +++ b/test/spec/modules/c1xBidAdapter_spec.js @@ -0,0 +1,189 @@ +import { expect } from 'chai'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { c1xAdapter } from '../../../modules/c1xBidAdapter.js'; + +const ENDPOINT = 'https://hb-stg.c1exchange.com/ht'; +const BIDDER_CODE = 'c1x'; + +describe('C1XAdapter', () => { + const adapter = newBidder(c1xAdapter); + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + describe('isBidRequestValid', () => { + let bid = { + 'bidder': BIDDER_CODE, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'params': { + 'placementId': 'div-gpt-ad-1654594619717-0' + } + }; + it('should return true when required params found', function () { + expect(c1xAdapter.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when placementId not passed correctly', function () { + bid.params.placementId = undefined; + expect(c1xAdapter.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when require params are not passed', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(c1xAdapter.isBidRequestValid(bid)).to.equal(false); + }); + }); + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': BIDDER_CODE, + 'params': { + 'placementId': 'div-gpt-ad-1654594619717-0', + 'dealId': '1233' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250], [300, 600] + ] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + const parseRequest = (data) => { + const parsedData = '{"' + data.replace(/=|&/g, (foundChar) => { + if (foundChar == '=') return '":"'; + else if (foundChar == '&') return '","'; + }) + '"}' + return parsedData; + }; + it('sends bid request to ENDPOINT via GET', () => { + const request = c1xAdapter.buildRequests(bidRequests); + expect(request[0].url).to.contain(ENDPOINT); + expect(request[0].method).to.equal('GET'); + }); + it('should generate correct bid Id tag', () => { + const request = c1xAdapter.buildRequests(bidRequests)[0]; + expect(request.bids[0].adUnitCode).to.equal('adunit-code'); + expect(request.bids[0].bidId).to.equal('30b31c1838de1e'); + }); + it('should convert params to proper form and attach to request', () => { + const request = c1xAdapter.buildRequests(bidRequests)[0]; + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj.adunits).to.equal('1'); + expect(payloadObj.a1s).to.equal('300x250,300x600'); + expect(payloadObj.a1).to.equal('adunit-code'); + expect(payloadObj.a1d).to.equal('1233'); + }); + it('should convert floor price to proper form and attach to request', () => { + let bidRequest = Object.assign({}, + bidRequests[0], + { + 'params': { + 'placementId': 'div-gpt-ad-1654594619717-0', + 'dealId': '1233', + 'floorPriceMap': { + '300x250': 4.35 + } + } + }); + const request = c1xAdapter.buildRequests([bidRequest])[0]; + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj.a1p).to.equal('4.35'); + }); + it('should convert pageurl to proper form and attach to request', () => { + let bidRequest = Object.assign({}, + bidRequests[0], + { + 'params': { + 'placementId': 'div-gpt-ad-1654594619717-0', + 'dealId': '1233', + 'pageurl': 'http://c1exchange.com/' + } + }); + + let bidderRequest = { + 'bidderCode': 'c1x' + } + bidderRequest.bids = bidRequests; + const request = c1xAdapter.buildRequests([bidRequest], bidderRequest)[0]; + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj.pageurl).to.equal('http://c1exchange.com/'); + }); + + it('should convert GDPR Consent to proper form and attach to request', () => { + let consentString = 'BOP2gFWOQIFovABABAENBGAAAAAAMw'; + let bidderRequest = { + 'bidderCode': 'c1x', + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true + } + } + bidderRequest.bids = bidRequests; + + const request = c1xAdapter.buildRequests(bidRequests, bidderRequest)[0]; + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj['consent_string']).to.equal('BOP2gFWOQIFovABABAENBGAAAAAAMw'); + expect(payloadObj['consent_required']).to.equal('true'); + }); + }); + + describe('interpretResponse', () => { + let response = { + 'bid': true, + 'cpm': 1.5, + 'ad': '', + 'width': 300, + 'height': 250, + 'crid': '8888', + 'adId': 'c1x-test', + 'bidType': 'GROSS_BID' + }; + it('should get correct bid response', () => { + let expectedResponse = [ + { + width: 300, + height: 250, + cpm: 1.5, + ad: '', + creativeId: '8888', + currency: 'USD', + ttl: 300, + netRevenue: false, + requestId: 'yyyy' + } + ]; + let bidderRequest = {}; + bidderRequest.bids = [ + { + adUnitCode: 'c1x-test', + bidId: 'yyyy' + } + ]; + let result = c1xAdapter.interpretResponse({ body: [response] }, bidderRequest); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + it('handles nobid responses', () => { + let response = { + bid: false, + adId: 'c1x-test' + }; + let bidderRequest = {}; + let result = c1xAdapter.interpretResponse({ body: [response] }, bidderRequest); + expect(result.length).to.equal(0); + }); + }); +}); From 7d127441c5df7fc760c78e92fb79fd7618fb8f15 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:34:34 +0200 Subject: [PATCH 096/569] bidWatch Analytics Adapter : add creative endpoint (#8710) * New Analytics Adapter bidwatch * test for bidwatch Analytics Adapter * change maintainer address * Update bidwatchAnalyticsAdapter.js * Update bidwatchAnalyticsAdapter.js * Update bidwatchAnalyticsAdapter.md * Update bidwatchAnalyticsAdapter.md * add features to bidwatchAnalyticsAdapter * update tests * add test and made improvements --- modules/bidwatchAnalyticsAdapter.js | 81 ++++++++++++++++--- .../modules/bidwatchAnalyticsAdapter_spec.js | 18 ++++- 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index 26a8c370af3..138f0533238 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -10,10 +10,14 @@ const { EVENTS: { AUCTION_END, BID_WON, + BID_RESPONSE, + BID_REQUESTED, } } = CONSTANTS; +let saveEvents = {} let allEvents = {} +let auctionEnd = {} let initOptions = {} let endpoint = 'https://default' let objectToSearchForBidderCode = ['bidderRequests', 'bidsReceived', 'noBids'] @@ -22,38 +26,87 @@ function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; } -function setOriginalBidder(arg) { +function cleanArgObject(arg, removead) { + if (typeof arg['bidderCode'] == 'string') { arg['originalBidder'] = getAdapterNameForAlias(arg['bidderCode']); } + if (typeof arg['creativeId'] == 'number') { + arg['creativeId'] = arg['creativeId'].toString(); + } + if (removead && typeof arg['ad'] != 'undefined') { + arg['ad'] = 'emptied'; + } + if (typeof arg['gdprConsent'] != 'undefined' && typeof arg['gdprConsent']['vendorData'] != 'undefined') { + arg['gdprConsent']['vendorData'] = 'emptied'; + } + return arg; +} + +function cleanArgs(arg, removead) { Object.keys(arg).forEach(key => { - arg[key]['originalBidder'] = getAdapterNameForAlias(arg[key]['bidderCode']); - if (typeof arg[key]['creativeId'] == 'number') { arg[key]['creativeId'] = arg[key]['creativeId'].toString(); } + arg[key] = cleanArgObject(arg[key], removead); }); return arg } -function checkBidderCode(args) { +function checkBidderCode(args, removead) { if (typeof args == 'object') { for (let i = 0; i < objectToSearchForBidderCode.length; i++) { - if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = setOriginalBidder(args[objectToSearchForBidderCode[i]]) } + if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = cleanArgs(args[objectToSearchForBidderCode[i]], removead) } } } if (typeof args['bidderCode'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidderCode']); } else if (typeof args['bidder'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidder']); } if (typeof args['creativeId'] == 'number') { args['creativeId'] = args['creativeId'].toString(); } + return args } function addEvent(eventType, args) { - if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } - if (eventType && args) { args = checkBidderCode(args); } - allEvents[eventType].push(args); + let argsCleaned; + if (eventType && args) { + if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } + argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), false); + allEvents[eventType].push(argsCleaned); + saveEvents[eventType].push(argsCleaned); + argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), true); + if (['auctionend', 'bidtimeout'].includes(eventType.toLowerCase())) { + if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } + auctionEnd[eventType].push(argsCleaned); + } + } } function handleBidWon(args) { - if (typeof allEvents.bidRequested == 'object' && allEvents.bidRequested.length > 0 && allEvents.bidRequested[0].gdprConsent) { args.gdpr = allEvents.bidRequested[0].gdprConsent; } + args = cleanArgObject(JSON.parse(JSON.stringify(args)), true); + let increment = args['cpm']; + if (typeof saveEvents['auctionEnd'] == 'object') { + for (let i = 0; i < saveEvents['auctionEnd'].length; i++) { + let tmpAuction = saveEvents['auctionEnd'][i]; + if (tmpAuction['auctionId'] == args['auctionId'] && typeof tmpAuction['bidsReceived'] == 'object') { + for (let j = 0; j < tmpAuction['bidsReceived'].length; j++) { + let tmpBid = tmpAuction['bidsReceived'][j]; + if (tmpBid['transactionId'] == args['transactionId'] && tmpBid['adId'] != args['adId']) { + if (args['cpm'] < tmpBid['cpm']) { + increment = 0; + } else if (increment > args['cpm'] - tmpBid['cpm']) { + increment = args['cpm'] - tmpBid['cpm']; + } + } + } + } + } + } + args['cpmIncrement'] = increment; + if (typeof saveEvents.bidRequested == 'object' && saveEvents.bidRequested.length > 0 && saveEvents.bidRequested[0].gdprConsent) { args.gdpr = saveEvents.bidRequested[0].gdprConsent; } ajax(endpoint + '.bidwatch.io/analytics/bid_won', null, JSON.stringify(args), {method: 'POST', withCredentials: true}); } function handleAuctionEnd() { - ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(allEvents), {method: 'POST', withCredentials: true}); + ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); + auctionEnd = {} + if (typeof allEvents['bidResponse'] != 'undefined') { + for (let i = 0; i < allEvents['bidResponse'].length; i++) { ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(allEvents['bidResponse'][i]), {method: 'POST', withCredentials: true}); } + } + allEvents = {} } let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { @@ -61,14 +114,20 @@ let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { eventType, args }) { - addEvent(eventType, args); switch (eventType) { case AUCTION_END: + addEvent(eventType, args); handleAuctionEnd(); break; case BID_WON: handleBidWon(args); break; + case BID_RESPONSE: + addEvent(eventType, args); + break; + case BID_REQUESTED: + addEvent(eventType, args); + break; } }}); diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js index 1a322d131a9..f827f068bb3 100644 --- a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js @@ -106,7 +106,8 @@ describe('BidWatch Analytics', function () { 'gdprConsent': { 'consentString': 'CONSENT', 'gdprApplies': true, - 'apiVersion': 2 + 'apiVersion': 2, + 'vendorData': 'a lot of borring stuff', }, 'start': 1647424261189 }, @@ -281,7 +282,22 @@ describe('BidWatch Analytics', function () { }); events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); events.emit(constants.EVENTS.AUCTION_END, auctionEnd); + expect(server.requests.length).to.equal(1); + let message = JSON.parse(server.requests[0].requestBody); + expect(message).to.have.property('auctionEnd').exist; + expect(message.auctionEnd).to.have.lengthOf(1); + expect(message.auctionEnd[0]).to.have.property('bidsReceived').and.to.have.lengthOf(1); + expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('ad'); + expect(message.auctionEnd[0].bidsReceived[0].ad).to.equal('emptied'); + expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); + expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).to.have.property('vendorData'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent.vendorData).to.equal('emptied'); events.emit(constants.EVENTS.BID_WON, bidWon); + expect(server.requests.length).to.equal(2); + message = JSON.parse(server.requests[1].requestBody); + expect(message).to.have.property('ad').and.to.equal('emptied'); + expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); sinon.assert.callCount(bidwatchAnalytics.track, 3); }); }); From b9fe9a2991e8383b6c121e19fe1d859fbd80365f Mon Sep 17 00:00:00 2001 From: AndBeyondMediaHB <107686862+AndBeyondMediaHB@users.noreply.github.com> Date: Wed, 3 Aug 2022 19:42:33 +0300 Subject: [PATCH 097/569] BeyondMedia Bid Adapter: initial bid adapter release (#8682) * add andBeyondMedia adapter * update bidderCode --- modules/andBeyondMediaBidAdapter.js | 202 +++++++++ modules/andBeyondMediaBidAdapter.md | 79 ++++ .../modules/andBeyondMediaBidAdapter_spec.js | 400 ++++++++++++++++++ 3 files changed, 681 insertions(+) create mode 100644 modules/andBeyondMediaBidAdapter.js create mode 100644 modules/andBeyondMediaBidAdapter.md create mode 100644 test/spec/modules/andBeyondMediaBidAdapter_spec.js diff --git a/modules/andBeyondMediaBidAdapter.js b/modules/andBeyondMediaBidAdapter.js new file mode 100644 index 00000000000..57c141dc2aa --- /dev/null +++ b/modules/andBeyondMediaBidAdapter.js @@ -0,0 +1,202 @@ +import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'beyondmedia'; +const AD_URL = 'https://backend.andbeyond.media/pbjs'; +const SYNC_URL = 'https://cookies.andbeyond.media'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl || bid.vastXml); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); + default: + return false; + } +} + +function getPlacementReqData(bid) { + const { params, bidId, mediaTypes } = bid; + const schain = bid.schain || {}; + const { placementId } = params; + const bidfloor = getBidFloor(bid); + + const placement = { + bidId, + schain, + bidfloor + }; + + placement.placementId = placementId; + placement.type = 'publisher'; + + if (mediaTypes && mediaTypes[BANNER]) { + placement.adFormat = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.adFormat = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.native = mediaTypes[NATIVE]; + placement.adFormat = NATIVE; + } + + return placement; +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (err) { + logError(err); + return 0; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid = {}) => { + const { params, bidId, mediaTypes } = bid; + let valid = Boolean(bidId && params && params.placementId); + + if (mediaTypes && mediaTypes[BANNER]) { + valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); + } else if (mediaTypes && mediaTypes[VIDEO]) { + valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); + } else if (mediaTypes && mediaTypes[NATIVE]) { + valid = valid && Boolean(mediaTypes[NATIVE]); + } + + return valid; + }, + + buildRequests: (validBidRequests = [], bidderRequest = {}) => { + let deviceWidth = 0; + let deviceHeight = 0; + + let winLocation; + try { + const winTop = window.top; + deviceWidth = winTop.screen.width; + deviceHeight = winTop.screen.height; + winLocation = winTop.location; + } catch (e) { + logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + logMessage(e); + } + + let location = refferLocation || winLocation; + const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + const host = location.host; + const page = location.pathname; + const secure = location.protocol === 'https:' ? 1 : 0; + const placements = []; + const request = { + deviceWidth, + deviceHeight, + language, + secure, + host, + page, + placements, + coppa: config.getConfig('coppa') === true ? 1 : 0, + ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, + tmax: config.getConfig('bidderTimeout') + }; + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + placements.push(getPlacementReqData(bid)); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/andBeyondMediaBidAdapter.md b/modules/andBeyondMediaBidAdapter.md new file mode 100644 index 00000000000..ebb44a7779c --- /dev/null +++ b/modules/andBeyondMediaBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: AndBeyond.Media Bidder Adapter +Module Type: AndBeyond.Media Bidder Adapter +Maintainer: sysengg@andbeyond.media +``` + +# Description + +Connects to AndBeyond.Media exchange for bids. +AndBeyond.Media bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'beyondmedia', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'beyondmedia', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'beyondmedia', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/andBeyondMediaBidAdapter_spec.js b/test/spec/modules/andBeyondMediaBidAdapter_spec.js new file mode 100644 index 00000000000..b739bec288e --- /dev/null +++ b/test/spec/modules/andBeyondMediaBidAdapter_spec.js @@ -0,0 +1,400 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/andBeyondMediaBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'beyondmedia' + +describe('AndBeyondMediaBidAdapter', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative', + } + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + refererInfo: { + referer: 'https://test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + expect(spec.isBidRequestValid(bids[1])).to.be.true; + expect(spec.isBidRequestValid(bids[2])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://backend.andbeyond.media/pbjs'); + }); + + it('Returns general data valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('string'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([], bidderRequest); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cookies.andbeyond.media/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1---' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cookies.andbeyond.media/image?pbjs=1&ccpa_consent=1---&coppa=0') + }); + }); +}); From 7792685ad4787357058eacbd103478964f3877e3 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 3 Aug 2022 18:16:19 +0000 Subject: [PATCH 098/569] Prebid 7.9.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c29ddad3673..1033d517bad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.9.0-pre", + "version": "7.9.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 3c6622adf9b..772023d54fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.9.0-pre", + "version": "7.9.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 53c5f4ddc44e6a190cfb6d2cef85c9886e5c7331 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 3 Aug 2022 18:16:20 +0000 Subject: [PATCH 099/569] Increment version to 7.10.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1033d517bad..a8d1b0f9097 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.9.0", + "version": "7.10.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 772023d54fb..91a41436d32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.9.0", + "version": "7.10.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From db2a333f2c521824193771fc302ec64f0b1e1118 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 3 Aug 2022 12:39:42 -0700 Subject: [PATCH 100/569] Prebid core: improve library support; make AnalyticsAdapter a library (#8599) * Automatically resolve libraries and their dependencies * Move AnalyticsAdapter under libraries --- .gitignore | 2 +- allowedModules.js | 3 + gulpHelpers.js | 77 ++++++------------ gulpfile.js | 9 ++- libraries/LIBRARIES.md | 5 ++ .../analyticsAdapter}/AnalyticsAdapter.js | 11 ++- .../analyticsAdapter/examples}/example.js | 2 +- .../analyticsAdapter/examples}/example2.js | 2 +- .../examples}/libraries/example.js | 0 .../examples}/libraries/example2.js | 0 modules/.submodules.json | 12 --- modules/adWMGAnalyticsAdapter.js | 2 +- modules/adagioAnalyticsAdapter.js | 2 +- modules/adkernelAdnAnalyticsAdapter.js | 2 +- modules/adlooxAnalyticsAdapter.js | 2 +- modules/adomikAnalyticsAdapter.js | 2 +- modules/adxcgAnalyticsAdapter.js | 2 +- modules/adxpremiumAnalyticsAdapter.js | 2 +- modules/appierAnalyticsAdapter.js | 2 +- modules/atsAnalyticsAdapter.js | 2 +- modules/bidwatchAnalyticsAdapter.js | 2 +- modules/byDataAnalyticsAdapter.js | 2 +- modules/concertAnalyticsAdapter.js | 2 +- modules/datablocksAnalyticsAdapter.js | 2 +- modules/eplanningAnalyticsAdapter.js | 2 +- modules/fintezaAnalyticsAdapter.js | 2 +- modules/hadronAnalyticsAdapter.js | 2 +- modules/id5AnalyticsAdapter.js | 2 +- modules/invisiblyAnalyticsAdapter.js | 2 +- modules/kargoAnalyticsAdapter.js | 2 +- modules/konduitAnalyticsAdapter.js | 2 +- modules/livewrappedAnalyticsAdapter.js | 2 +- modules/liveyieldAnalyticsAdapter.js | 2 +- modules/malltvAnalyticsAdapter.js | 2 +- modules/marsmediaAnalyticsAdapter.js | 2 +- modules/medianetAnalyticsAdapter.js | 2 +- modules/ooloAnalyticsAdapter.js | 2 +- modules/openxAnalyticsAdapter.js | 2 +- modules/optimonAnalyticsAdapter.js | 2 +- modules/pianoDmpAnalyticsAdapter.js | 2 +- modules/prebidmanagerAnalyticsAdapter.js | 2 +- modules/pubmaticAnalyticsAdapter.js | 2 +- modules/pubperfAnalyticsAdapter.js | 2 +- modules/pubstackAnalyticsAdapter.js | 2 +- modules/pubwiseAnalyticsAdapter.js | 2 +- modules/pubxaiAnalyticsAdapter.js | 2 +- modules/pulsepointAnalyticsAdapter.js | 2 +- modules/realvuAnalyticsAdapter.js | 2 +- modules/relevantAnalyticsAdapter.js | 2 +- modules/rivrAnalyticsAdapter.js | 2 +- modules/roxotAnalyticsAdapter.js | 2 +- modules/rubiconAnalyticsAdapter.js | 2 +- modules/scaleableAnalyticsAdapter.js | 2 +- modules/sharethroughAnalyticsAdapter.js | 2 +- modules/sigmoidAnalyticsAdapter.js | 2 +- modules/sonobiAnalyticsAdapter.js | 2 +- modules/sovrnAnalyticsAdapter.js | 2 +- modules/staqAnalyticsAdapter.js | 2 +- modules/terceptAnalyticsAdapter.js | 2 +- modules/ucfunnelAnalyticsAdapter.js | 2 +- modules/yieldoneAnalyticsAdapter.js | 2 +- modules/yuktamediaAnalyticsAdapter.js | 2 +- modules/zeta_global_sspAnalyticsAdapter.js | 2 +- package-lock.json | 79 +++++++++++++++++++ package.json | 1 + test/mocks/analyticsStub.js | 2 +- test/spec/AnalyticsAdapter_spec.js | 2 +- webpack.conf.js | 58 +++++++++++--- 68 files changed, 227 insertions(+), 142 deletions(-) create mode 100644 libraries/LIBRARIES.md rename {src => libraries/analyticsAdapter}/AnalyticsAdapter.js (95%) rename {src/adapters/analytics => libraries/analyticsAdapter/examples}/example.js (85%) rename {src/adapters/analytics => libraries/analyticsAdapter/examples}/example2.js (92%) rename {src/adapters/analytics => libraries/analyticsAdapter/examples}/libraries/example.js (100%) rename {src/adapters/analytics => libraries/analyticsAdapter/examples}/libraries/example2.js (100%) diff --git a/.gitignore b/.gitignore index c0452b7b3d0..e5f000dd4d5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ selenium*.log integrationExamples/gpt/gpt.html integrationExamples/gpt/*-test.html integrationExamples/implementations/ -src/adapters/analytics/libraries +libraries/analyticsAdapter/examples/libraries # Coverage reports build/coverage/ diff --git a/allowedModules.js b/allowedModules.js index be9a2dc2abf..3e6e3039fa2 100644 --- a/allowedModules.js +++ b/allowedModules.js @@ -15,5 +15,8 @@ module.exports = { 'just-clone', 'dlv', 'dset' + ], + 'libraries': [ + ...sharedWhiteList // empty for now, but keep it to enable linting ] }; diff --git a/gulpHelpers.js b/gulpHelpers.js index 60e2fab0df2..1eec08b7a3e 100644 --- a/gulpHelpers.js +++ b/gulpHelpers.js @@ -6,12 +6,9 @@ const MANIFEST = 'package.json'; const through = require('through2'); const _ = require('lodash'); const gutil = require('gulp-util'); -const dependencyMap = require('./modules/.submodules.json'); -const submodules = dependencyMap.parentModules; -const libraries = dependencyMap.libraries; +const submodules = require('./modules/.submodules.json').parentModules; const MODULE_PATH = './modules'; -const LIBRARY_PATH = './libraries'; const BUILD_PATH = './build/dist'; const DEV_PATH = './build/dev'; const ANALYTICS_PATH = '../analytics'; @@ -71,68 +68,34 @@ module.exports = { } }); - Object.keys(libraries).forEach(library => { - if (!modules.includes(library) && modules.some(module => libraries[library].dependants.includes(module))) { - modules.unshift(library); - } - }); - return modules; }, - getParentLibraries(moduleName) { - const libraryNames = []; - Object.keys(libraries).forEach(libraryName => { - const library = libraries[libraryName]; - if (library.dependants.includes(moduleName)) { - libraryNames.push(libraryName); - } - }); - return libraryNames; - }, - getLibraryFiles(name) { - const library = libraries[name]; - const files = library.files.map((file) => path.resolve('./libraries/', name, file)) - return files; - }, - isLibrary(name) { - return !!libraries[name]; - }, getModules: _.memoize(function(externalModules) { externalModules = externalModules || []; var internalModules; try { - var getInternalModules = function(absolutePath) { - return fs.readdirSync(absolutePath) - .filter(file => (/^[^\.]+(\.js)?$/).test(file)) - .reduce((memo, file) => { - var moduleName = file.split(new RegExp('[.\\' + path.sep + ']'))[0]; - var modulePath = path.join(absolutePath, file); - if (fs.lstatSync(modulePath).isDirectory()) { - modulePath = path.join(modulePath, 'index.js') - } - if (fs.existsSync(modulePath)) { - memo[modulePath] = moduleName; - } - return memo; - }, {}); - }; - var absoluteModulePath = path.join(__dirname, MODULE_PATH); - var absoluteLibraryPath = path.join(__dirname, LIBRARY_PATH); - - internalModules = getInternalModules(absoluteModulePath); - var internalLibraries = getInternalModules(absoluteLibraryPath); - Object.assign(internalModules, internalLibraries); + internalModules = fs.readdirSync(absoluteModulePath) + .filter(file => (/^[^\.]+(\.js)?$/).test(file)) + .reduce((memo, file) => { + var moduleName = file.split(new RegExp('[.\\' + path.sep + ']'))[0]; + var modulePath = path.join(absoluteModulePath, file); + if (fs.lstatSync(modulePath).isDirectory()) { + modulePath = path.join(modulePath, 'index.js') + } + if (fs.existsSync(modulePath)) { + memo[modulePath] = moduleName; + } + return memo; + }, {}); } catch (err) { internalModules = {}; } return Object.assign(externalModules.reduce((memo, module) => { try { // prefer internal project modules before looking at project dependencies - var modulePath = require.resolve(module, {paths: [MODULE_PATH, LIBRARY_PATH]}); - if (modulePath === '') { - modulePath = require.resolve(module); - } + var modulePath = require.resolve(module, {paths: ['./modules']}); + if (modulePath === '') modulePath = require.resolve(module); memo[modulePath] = module; } catch (err) { @@ -142,16 +105,20 @@ module.exports = { }, internalModules)); }), + getBuiltPath(dev, assetPath) { + return path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, assetPath) + }, + getBuiltModules: function(dev, externalModules) { var modules = this.getModuleNames(externalModules); if (Array.isArray(externalModules)) { modules = _.intersection(modules, externalModules); } - return modules.map(name => path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, name + '.js')); + return modules.map(name => this.getBuiltPath(dev, name + '.js')); }, getBuiltPrebidCoreFile: function(dev) { - return path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, 'prebid-core' + '.js'); + return this.getBuiltPath(dev, 'prebid-core.js') }, getModulePaths: function(externalModules) { diff --git a/gulpfile.js b/gulpfile.js index 39181c92316..3425850286d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -240,8 +240,15 @@ function bundle(dev, moduleArr) { }); } } + const coreFile = helpers.getBuiltPrebidCoreFile(dev); + const moduleFiles = helpers.getBuiltModules(dev, modules); + const depGraph = require(helpers.getBuiltPath(dev, 'dependencies.json')); + const dependencies = new Set(); + [coreFile].concat(moduleFiles).map(name => path.basename(name)).forEach((file) => { + (depGraph[file] || []).forEach((dep) => dependencies.add(helpers.getBuiltPath(dev, dep))); + }) - var entries = [helpers.getBuiltPrebidCoreFile(dev)].concat(helpers.getBuiltModules(dev, modules)); + const entries = [coreFile].concat(Array.from(dependencies), moduleFiles); var outputFileName = argv.bundleName ? argv.bundleName : 'prebid.js'; diff --git a/libraries/LIBRARIES.md b/libraries/LIBRARIES.md new file mode 100644 index 00000000000..e4d8fcc4f98 --- /dev/null +++ b/libraries/LIBRARIES.md @@ -0,0 +1,5 @@ +## Cross-module libraries + +Each directory under this one is packaged into a "library" during the build. + +Modules may share code by simply importing from a common library file; if the module is included in the build, any libraries they import from will also be included. diff --git a/src/AnalyticsAdapter.js b/libraries/analyticsAdapter/AnalyticsAdapter.js similarity index 95% rename from src/AnalyticsAdapter.js rename to libraries/analyticsAdapter/AnalyticsAdapter.js index 47f56dc054f..277a1455a75 100644 --- a/src/AnalyticsAdapter.js +++ b/libraries/analyticsAdapter/AnalyticsAdapter.js @@ -1,7 +1,7 @@ -import CONSTANTS from './constants.json'; -import { ajax } from './ajax.js'; -import { logMessage, _each } from './utils.js'; -import * as events from './events.js' +import CONSTANTS from '../../src/constants.json'; +import { ajax } from '../../src/ajax.js'; +import { logMessage, _each } from '../../src/utils.js'; +import * as events from '../../src/events.js' export const _internal = { ajax @@ -31,14 +31,13 @@ const { const ENDPOINT = 'endpoint'; const BUNDLE = 'bundle'; -var _sampled = true; - export default function AnalyticsAdapter({ url, analyticsType, global, handler }) { const _queue = []; let _eventCount = 0; let _enableCheck = true; let _handlers; let _enabled = false; + let _sampled = true; if (analyticsType === ENDPOINT || BUNDLE) { _emptyQueue(); diff --git a/src/adapters/analytics/example.js b/libraries/analyticsAdapter/examples/example.js similarity index 85% rename from src/adapters/analytics/example.js rename to libraries/analyticsAdapter/examples/example.js index 1321612b688..c6907907b23 100644 --- a/src/adapters/analytics/example.js +++ b/libraries/analyticsAdapter/examples/example.js @@ -2,7 +2,7 @@ * example.js - analytics adapter for Example Analytics Library example */ -import adapter from '../../AnalyticsAdapter.js'; +import adapter from '../AnalyticsAdapter.js'; export default adapter( { diff --git a/src/adapters/analytics/example2.js b/libraries/analyticsAdapter/examples/example2.js similarity index 92% rename from src/adapters/analytics/example2.js rename to libraries/analyticsAdapter/examples/example2.js index eadf994ce36..d95a3f54283 100644 --- a/src/adapters/analytics/example2.js +++ b/libraries/analyticsAdapter/examples/example2.js @@ -5,7 +5,7 @@ import { ajax } from '../../../src/ajax.js'; * example2.js - analytics adapter for Example2 Analytics Endpoint example */ -import adapter from '../../AnalyticsAdapter.js'; +import adapter from '../AnalyticsAdapter.js'; const url = 'https://httpbin.org/post'; const analyticsType = 'endpoint'; diff --git a/src/adapters/analytics/libraries/example.js b/libraries/analyticsAdapter/examples/libraries/example.js similarity index 100% rename from src/adapters/analytics/libraries/example.js rename to libraries/analyticsAdapter/examples/libraries/example.js diff --git a/src/adapters/analytics/libraries/example2.js b/libraries/analyticsAdapter/examples/libraries/example2.js similarity index 100% rename from src/adapters/analytics/libraries/example2.js rename to libraries/analyticsAdapter/examples/libraries/example2.js diff --git a/modules/.submodules.json b/modules/.submodules.json index b031a6bac02..72eadf15d31 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -73,17 +73,5 @@ "validationFpdModule", "topicsFpdModule" ] - }, - "libraries": { - "getOrigin": { - "files": [ - "./index.js" - ], - "dependants": [ - "ooloAnalyticsAdapter", - "resetdigitalBidAdapter", - "rtbhouseBidAdapter.js" - ] - } } } \ No newline at end of file diff --git a/modules/adWMGAnalyticsAdapter.js b/modules/adWMGAnalyticsAdapter.js index 8183187eb73..dd0340071d1 100644 --- a/modules/adWMGAnalyticsAdapter.js +++ b/modules/adWMGAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; diff --git a/modules/adagioAnalyticsAdapter.js b/modules/adagioAnalyticsAdapter.js index f929f7e660b..96f3f089cbd 100644 --- a/modules/adagioAnalyticsAdapter.js +++ b/modules/adagioAnalyticsAdapter.js @@ -2,7 +2,7 @@ * Analytics Adapter for Adagio */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { getWindowTop } from '../src/utils.js'; diff --git a/modules/adkernelAdnAnalyticsAdapter.js b/modules/adkernelAdnAnalyticsAdapter.js index d4aa3b035e0..901c0d2fd98 100644 --- a/modules/adkernelAdnAnalyticsAdapter.js +++ b/modules/adkernelAdnAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { logError, parseUrl, _each } from '../src/utils.js'; diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js index a26a5e507e6..5db75c656bb 100644 --- a/modules/adlooxAnalyticsAdapter.js +++ b/modules/adlooxAnalyticsAdapter.js @@ -5,7 +5,7 @@ */ import adapterManager from '../src/adapterManager.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import {loadExternalScript} from '../src/adloader.js'; import {auctionManager} from '../src/auctionManager.js'; import {AUCTION_COMPLETED} from '../src/auction.js'; diff --git a/modules/adomikAnalyticsAdapter.js b/modules/adomikAnalyticsAdapter.js index 40809c59093..27a6821d9f5 100644 --- a/modules/adomikAnalyticsAdapter.js +++ b/modules/adomikAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {logInfo} from '../src/utils.js'; diff --git a/modules/adxcgAnalyticsAdapter.js b/modules/adxcgAnalyticsAdapter.js index 5cd04ce13cd..f3bb1270334 100644 --- a/modules/adxcgAnalyticsAdapter.js +++ b/modules/adxcgAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { parseSizesInput, uniques, buildUrl, logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/adxpremiumAnalyticsAdapter.js b/modules/adxpremiumAnalyticsAdapter.js index 4db01024208..456180a964b 100644 --- a/modules/adxpremiumAnalyticsAdapter.js +++ b/modules/adxpremiumAnalyticsAdapter.js @@ -1,6 +1,6 @@ import {deepClone, logError, logInfo} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {includes} from '../src/polyfill.js'; diff --git a/modules/appierAnalyticsAdapter.js b/modules/appierAnalyticsAdapter.js index afcf63ef2c1..b4081feaf92 100644 --- a/modules/appierAnalyticsAdapter.js +++ b/modules/appierAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {getGlobal} from '../src/prebidGlobal.js'; diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index 390e06366d1..0c0227ea34a 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { logError, logInfo } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adaptermanager from '../src/adapterManager.js'; import {ajax} from '../src/ajax.js'; diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index 138f0533238..ef63fee53d5 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; diff --git a/modules/byDataAnalyticsAdapter.js b/modules/byDataAnalyticsAdapter.js index 1bf47cd467f..ccf57942d7a 100644 --- a/modules/byDataAnalyticsAdapter.js +++ b/modules/byDataAnalyticsAdapter.js @@ -2,7 +2,7 @@ import { deepClone, logInfo, logError } from '../src/utils.js'; import Base64 from 'crypto-js/enc-base64'; import hmacSHA512 from 'crypto-js/hmac-sha512'; import enc from 'crypto-js/enc-utf8'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/concertAnalyticsAdapter.js b/modules/concertAnalyticsAdapter.js index cd52a2ffabf..210d1898338 100644 --- a/modules/concertAnalyticsAdapter.js +++ b/modules/concertAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { logMessage } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; diff --git a/modules/datablocksAnalyticsAdapter.js b/modules/datablocksAnalyticsAdapter.js index 5e977155284..61933cc45e9 100644 --- a/modules/datablocksAnalyticsAdapter.js +++ b/modules/datablocksAnalyticsAdapter.js @@ -2,7 +2,7 @@ * Analytics Adapter for Datablocks */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; var datablocksAdapter = adapter({ diff --git a/modules/eplanningAnalyticsAdapter.js b/modules/eplanningAnalyticsAdapter.js index 365f91382f7..9eb701b8ecc 100644 --- a/modules/eplanningAnalyticsAdapter.js +++ b/modules/eplanningAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { logError } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/fintezaAnalyticsAdapter.js b/modules/fintezaAnalyticsAdapter.js index 10b59de1838..7b1b2f69364 100644 --- a/modules/fintezaAnalyticsAdapter.js +++ b/modules/fintezaAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { parseUrl, logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/hadronAnalyticsAdapter.js b/modules/hadronAnalyticsAdapter.js index c0e39925b4a..073dd15ea49 100644 --- a/modules/hadronAnalyticsAdapter.js +++ b/modules/hadronAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/id5AnalyticsAdapter.js b/modules/id5AnalyticsAdapter.js index 69e303b520a..5ae04eb57ce 100644 --- a/modules/id5AnalyticsAdapter.js +++ b/modules/id5AnalyticsAdapter.js @@ -1,4 +1,4 @@ -import buildAdapter from '../src/AnalyticsAdapter.js'; +import buildAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { ajax } from '../src/ajax.js'; diff --git a/modules/invisiblyAnalyticsAdapter.js b/modules/invisiblyAnalyticsAdapter.js index 1f0bbfd46c3..a4f4eba271c 100644 --- a/modules/invisiblyAnalyticsAdapter.js +++ b/modules/invisiblyAnalyticsAdapter.js @@ -2,7 +2,7 @@ * invisiblyAdapterAdapter.js - analytics adapter for Invisibly */ import { ajaxBuilder } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { generateUUID, logInfo } from '../src/utils.js'; diff --git a/modules/kargoAnalyticsAdapter.js b/modules/kargoAnalyticsAdapter.js index 8ddf14aaa83..652e105167d 100644 --- a/modules/kargoAnalyticsAdapter.js +++ b/modules/kargoAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/konduitAnalyticsAdapter.js b/modules/konduitAnalyticsAdapter.js index f8a44d7cc94..a1a586b25db 100644 --- a/modules/konduitAnalyticsAdapter.js +++ b/modules/konduitAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { parseSizesInput, logError, uniques } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { targeting } from '../src/targeting.js'; import { config } from '../src/config.js'; diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index 1116fd99ba0..2721031948d 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { timestamp, logInfo, getWindowTop } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { getGlobal } from '../src/prebidGlobal.js'; diff --git a/modules/liveyieldAnalyticsAdapter.js b/modules/liveyieldAnalyticsAdapter.js index 411b76a5149..997c0eaace0 100644 --- a/modules/liveyieldAnalyticsAdapter.js +++ b/modules/liveyieldAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { logError } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/malltvAnalyticsAdapter.js b/modules/malltvAnalyticsAdapter.js index a0e2a208bc9..af903795e49 100644 --- a/modules/malltvAnalyticsAdapter.js +++ b/modules/malltvAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js' -import adapter from '../src/AnalyticsAdapter.js' +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js' import CONSTANTS from '../src/constants.json' import adapterManager from '../src/adapterManager.js' import {getGlobal} from '../src/prebidGlobal.js' diff --git a/modules/marsmediaAnalyticsAdapter.js b/modules/marsmediaAnalyticsAdapter.js index 12c333631a2..c86cc4dfbc2 100644 --- a/modules/marsmediaAnalyticsAdapter.js +++ b/modules/marsmediaAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; /**** diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index 05c18a47f94..b7abe5f56cf 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -11,7 +11,7 @@ import { uniques, getHighestCpm } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {ajax} from '../src/ajax.js'; diff --git a/modules/ooloAnalyticsAdapter.js b/modules/ooloAnalyticsAdapter.js index 6f9d5620d53..9bc140f0536 100644 --- a/modules/ooloAnalyticsAdapter.js +++ b/modules/ooloAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { _each, deepClone, pick, deepSetValue, logError, logInfo } from '../src/utils.js'; import { getOrigin } from '../libraries/getOrigin/index.js'; -import adapter from '../src/AnalyticsAdapter.js' +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js' import adapterManager from '../src/adapterManager.js' import CONSTANTS from '../src/constants.json' import { ajax } from '../src/ajax.js' diff --git a/modules/openxAnalyticsAdapter.js b/modules/openxAnalyticsAdapter.js index 89140c0aacd..7cc71fe898e 100644 --- a/modules/openxAnalyticsAdapter.js +++ b/modules/openxAnalyticsAdapter.js @@ -13,7 +13,7 @@ import { parseSizesInput, uniques } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {ajax} from '../src/ajax.js'; diff --git a/modules/optimonAnalyticsAdapter.js b/modules/optimonAnalyticsAdapter.js index 34b2778afc9..82bc18f605d 100644 --- a/modules/optimonAnalyticsAdapter.js +++ b/modules/optimonAnalyticsAdapter.js @@ -8,7 +8,7 @@ * */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const optimonAnalyticsAdapter = adapter({ diff --git a/modules/pianoDmpAnalyticsAdapter.js b/modules/pianoDmpAnalyticsAdapter.js index 08ef5406d9f..47159475b5d 100644 --- a/modules/pianoDmpAnalyticsAdapter.js +++ b/modules/pianoDmpAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const pianoDmpAnalytics = adapter({ analyticsType: 'bundle', handler: 'on' }); diff --git a/modules/prebidmanagerAnalyticsAdapter.js b/modules/prebidmanagerAnalyticsAdapter.js index 6235b10fa13..708d2b79243 100644 --- a/modules/prebidmanagerAnalyticsAdapter.js +++ b/modules/prebidmanagerAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { generateUUID, getParameterByName, logError, parseUrl, logInfo } from '../src/utils.js'; import {ajaxBuilder} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 6a73838ff1d..48b570a683b 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { _each, pick, logWarn, isStr, isArray, logError } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; diff --git a/modules/pubperfAnalyticsAdapter.js b/modules/pubperfAnalyticsAdapter.js index 9282d5814c0..9ef95adb77a 100644 --- a/modules/pubperfAnalyticsAdapter.js +++ b/modules/pubperfAnalyticsAdapter.js @@ -2,7 +2,7 @@ * Analytics Adapter for Pubperf */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { logError } from '../src/utils.js'; diff --git a/modules/pubstackAnalyticsAdapter.js b/modules/pubstackAnalyticsAdapter.js index b1da40c5b89..ef33b2d75ae 100644 --- a/modules/pubstackAnalyticsAdapter.js +++ b/modules/pubstackAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const pubstackAnalytics = adapter({ diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index 9b40d45542c..7976bcef58d 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { getParameterByName, logInfo, generateUUID, debugTurnedOn } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index 3f35cd8ff79..bd92162b8cb 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { deepAccess, getGptSlotInfoForAdUnitCode, parseSizesInput, getWindowLocation, buildUrl } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/pulsepointAnalyticsAdapter.js b/modules/pulsepointAnalyticsAdapter.js index 375a817f257..999ae3dd3de 100644 --- a/modules/pulsepointAnalyticsAdapter.js +++ b/modules/pulsepointAnalyticsAdapter.js @@ -2,7 +2,7 @@ * pulsepoint.js - Analytics Adapter for PulsePoint */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; var pulsepointAdapter = adapter({ diff --git a/modules/realvuAnalyticsAdapter.js b/modules/realvuAnalyticsAdapter.js index 832e907893c..0811c70a2f7 100644 --- a/modules/realvuAnalyticsAdapter.js +++ b/modules/realvuAnalyticsAdapter.js @@ -1,5 +1,5 @@ // RealVu Analytics Adapter -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/relevantAnalyticsAdapter.js b/modules/relevantAnalyticsAdapter.js index 5917262c810..7774370f7ad 100644 --- a/modules/relevantAnalyticsAdapter.js +++ b/modules/relevantAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const relevantAnalytics = adapter({ analyticsType: 'bundle', handler: 'on' }); diff --git a/modules/rivrAnalyticsAdapter.js b/modules/rivrAnalyticsAdapter.js index 279b1b13051..cf14ff44571 100644 --- a/modules/rivrAnalyticsAdapter.js +++ b/modules/rivrAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; diff --git a/modules/roxotAnalyticsAdapter.js b/modules/roxotAnalyticsAdapter.js index b11898b9ea8..1ded17f3a5b 100644 --- a/modules/roxotAnalyticsAdapter.js +++ b/modules/roxotAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {deepClone, getParameterByName, logError, logInfo} from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {includes} from '../src/polyfill.js'; diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index ca0d0c1fe65..3ad1bb26b47 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { generateUUID, mergeDeep, deepAccess, parseUrl, logError, pick, isEmpty, logWarn, debugTurnedOn, parseQS, getWindowLocation, isAdUnitCodeMatchingSlot, isNumber, isGptPubadsDefined, _each, deepSetValue, deepClone, logInfo } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; diff --git a/modules/scaleableAnalyticsAdapter.js b/modules/scaleableAnalyticsAdapter.js index d7379462e0d..46f9d45d84d 100644 --- a/modules/scaleableAnalyticsAdapter.js +++ b/modules/scaleableAnalyticsAdapter.js @@ -2,7 +2,7 @@ import { ajax } from '../src/ajax.js'; import CONSTANTS from '../src/constants.json'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { logMessage } from '../src/utils.js'; diff --git a/modules/sharethroughAnalyticsAdapter.js b/modules/sharethroughAnalyticsAdapter.js index 4f065cbca23..6502c7e3a53 100644 --- a/modules/sharethroughAnalyticsAdapter.js +++ b/modules/sharethroughAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { tryAppendQueryString } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const emptyUrl = ''; diff --git a/modules/sigmoidAnalyticsAdapter.js b/modules/sigmoidAnalyticsAdapter.js index a0521bd5297..dc386813978 100644 --- a/modules/sigmoidAnalyticsAdapter.js +++ b/modules/sigmoidAnalyticsAdapter.js @@ -1,7 +1,7 @@ /* Sigmoid Analytics Adapter for prebid.js v1.1.0-pre Updated : 2018-03-28 */ import {includes} from '../src/polyfill.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; diff --git a/modules/sonobiAnalyticsAdapter.js b/modules/sonobiAnalyticsAdapter.js index 0de6647149a..75d481aba9e 100644 --- a/modules/sonobiAnalyticsAdapter.js +++ b/modules/sonobiAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { deepClone, logInfo, logError } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {ajaxBuilder} from '../src/ajax.js'; diff --git a/modules/sovrnAnalyticsAdapter.js b/modules/sovrnAnalyticsAdapter.js index 8a5b816ef43..a72c4b1a5a5 100644 --- a/modules/sovrnAnalyticsAdapter.js +++ b/modules/sovrnAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {logError, timestamp} from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adaptermanager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {ajaxBuilder} from '../src/ajax.js'; diff --git a/modules/staqAnalyticsAdapter.js b/modules/staqAnalyticsAdapter.js index b9bfe5212c6..69d23a3a604 100644 --- a/modules/staqAnalyticsAdapter.js +++ b/modules/staqAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { logInfo, logError, parseUrl, _each } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; diff --git a/modules/terceptAnalyticsAdapter.js b/modules/terceptAnalyticsAdapter.js index 748e512bd42..9d215f0ceda 100644 --- a/modules/terceptAnalyticsAdapter.js +++ b/modules/terceptAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { parseSizesInput, getWindowLocation, buildUrl } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/ucfunnelAnalyticsAdapter.js b/modules/ucfunnelAnalyticsAdapter.js index 7a471a1d3b4..77fffddbaae 100644 --- a/modules/ucfunnelAnalyticsAdapter.js +++ b/modules/ucfunnelAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {getGlobal} from '../src/prebidGlobal.js'; diff --git a/modules/yieldoneAnalyticsAdapter.js b/modules/yieldoneAnalyticsAdapter.js index 126e3504f98..0663ecb3f76 100644 --- a/modules/yieldoneAnalyticsAdapter.js +++ b/modules/yieldoneAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { isArray, deepClone } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { targeting } from '../src/targeting.js'; diff --git a/modules/yuktamediaAnalyticsAdapter.js b/modules/yuktamediaAnalyticsAdapter.js index 8ff22faa7f4..31c6daae7f6 100644 --- a/modules/yuktamediaAnalyticsAdapter.js +++ b/modules/yuktamediaAnalyticsAdapter.js @@ -1,6 +1,6 @@ import {buildUrl, generateUUID, getWindowLocation, logError, logInfo, parseSizesInput, parseUrl} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {getStorageManager} from '../src/storageManager.js'; diff --git a/modules/zeta_global_sspAnalyticsAdapter.js b/modules/zeta_global_sspAnalyticsAdapter.js index 906e6e19cc2..ee3bb9cd5d6 100644 --- a/modules/zeta_global_sspAnalyticsAdapter.js +++ b/modules/zeta_global_sspAnalyticsAdapter.js @@ -3,7 +3,7 @@ import { ajax } from '../src/ajax.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; const ZETA_GVL_ID = 833; const ADAPTER_CODE = 'zeta_global_ssp'; diff --git a/package-lock.json b/package-lock.json index a8d1b0f9097..0e221a988fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,6 +99,7 @@ "webdriverio": "^7.6.1", "webpack": "^5.70.0", "webpack-bundle-analyzer": "^4.5.0", + "webpack-manifest-plugin": "^5.0.0", "webpack-stream": "^7.0.0", "yargs": "^1.3.1" }, @@ -20391,6 +20392,12 @@ "node": ">=10.0.0" } }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -22907,6 +22914,44 @@ } } }, + "node_modules/webpack-manifest-plugin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.0.tgz", + "integrity": "sha512-8RQfMAdc5Uw3QbCQ/CBV/AXqOR8mt03B6GJmRbhWopE8GzRfEpn+k0ZuWywxW+5QZsffhmFDY1J6ohqJo+eMuw==", + "dev": true, + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "webpack": "^5.47.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", @@ -39479,6 +39524,12 @@ "debug": "~4.3.1" } }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -41524,6 +41575,34 @@ } } }, + "webpack-manifest-plugin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.0.tgz", + "integrity": "sha512-8RQfMAdc5Uw3QbCQ/CBV/AXqOR8mt03B6GJmRbhWopE8GzRfEpn+k0ZuWywxW+5QZsffhmFDY1J6ohqJo+eMuw==", + "dev": true, + "requires": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dev": true, + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + } + } + } + }, "webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", diff --git a/package.json b/package.json index 91a41436d32..f3bbc12d2d5 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "webdriverio": "^7.6.1", "webpack": "^5.70.0", "webpack-bundle-analyzer": "^4.5.0", + "webpack-manifest-plugin": "^5.0.0", "webpack-stream": "^7.0.0", "yargs": "^1.3.1" }, diff --git a/test/mocks/analyticsStub.js b/test/mocks/analyticsStub.js index 1023db882e8..8507c5a6275 100644 --- a/test/mocks/analyticsStub.js +++ b/test/mocks/analyticsStub.js @@ -1,4 +1,4 @@ -import {_internal} from '../../src/AnalyticsAdapter.js'; +import {_internal} from '../../libraries/analyticsAdapter/AnalyticsAdapter.js'; before(() => { // stub out analytics networking to avoid random events polluting the global xhr mock diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index cdd17ea89ad..06ab8838985 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -14,7 +14,7 @@ const AD_RENDER_SUCCEEDED = CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED; const AUCTION_DEBUG = CONSTANTS.EVENTS.AUCTION_DEBUG; const ADD_AD_UNITS = CONSTANTS.EVENTS.ADD_AD_UNITS; -const AnalyticsAdapter = require('src/AnalyticsAdapter').default; +const AnalyticsAdapter = require('libraries/analyticsAdapter/AnalyticsAdapter.js').default; const config = { url: 'https://localhost:9999/endpoint', analyticsType: 'endpoint' diff --git a/webpack.conf.js b/webpack.conf.js index deb8ad94e51..0ead550e446 100644 --- a/webpack.conf.js +++ b/webpack.conf.js @@ -5,10 +5,30 @@ var webpack = require('webpack'); var helpers = require('./gulpHelpers.js'); var { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); var argv = require('yargs').argv; +const fs = require('fs'); const babelConfig = require('./babelConfig.js')({disableFeatures: helpers.getDisabledFeatures(), prebidDistUrlBase: argv.distUrlBase}); +const {WebpackManifestPlugin} = require('webpack-manifest-plugin') var plugins = [ new webpack.EnvironmentPlugin({'LiveConnectMode': null}), + new WebpackManifestPlugin({ + fileName: 'dependencies.json', + generate: (seed, files) => { + const entries = new Set(); + const addEntry = entries.add.bind(entries); + + files.forEach(file => file.chunk && file.chunk._groups && file.chunk._groups.forEach(addEntry)); + + return Array.from(entries).reduce((acc, entry) => { + const name = (entry.options || {}).name || (entry.runtimeChunk || {}).name + const files = (entry.chunks || []) + .filter(chunk => chunk.name !== name) + .flatMap(chunk => [...chunk.files]) + .filter(Boolean); + return name && files.length ? {...acc, [`${name}.js`]: files} : acc + }, seed) + } + }) ]; if (argv.analyze) { @@ -44,16 +64,6 @@ module.exports = { dependOn: 'prebid-core' }; - if (helpers.isLibrary(mod)) { - const libraryFiles = helpers.getLibraryFiles(mod); - moduleEntry.import = libraryFiles || moduleEntry.import; - } - - const libraries = helpers.getParentLibraries(mod); - if (libraries.length) { - moduleEntry.dependOn = ['prebid-core'].concat(libraries); - } - entry[mod] = moduleEntry; } }); @@ -97,7 +107,33 @@ module.exports = { module: true, // do not prepend every module with 'use strict'; allow mangling of top-level locals } }) - ] + ], + splitChunks: { + chunks: 'initial', + minChunks: 1, + minSize: 0, + cacheGroups: (() => { + const libRoot = path.resolve(__dirname, 'libraries'); + const libraries = Object.fromEntries( + fs.readdirSync(libRoot) + .filter((f) => fs.lstatSync(path.resolve(libRoot, f)).isDirectory()) + .map(lib => { + const dir = path.resolve(libRoot, lib) + const def = { + name: lib, + test: (module) => { + return module.resource && module.resource.startsWith(dir) + } + } + return [lib, def]; + }) + ); + return Object.assign(libraries, { + default: false, + defaultVendors: false + }) + })() + } }, plugins }; From 301b07260123283af5c7378385550a77081f505a Mon Sep 17 00:00:00 2001 From: eknis Date: Thu, 4 Aug 2022 13:18:00 +0900 Subject: [PATCH 101/569] Intimate Merger Universal Identifier System: add imppid (#8767) * add imuidModule "imppid" * update response * fix test * update spec * add GAM ppid test for imppid --- modules/imuIdSystem.js | 66 ++++++++++++++++----------- modules/imuIdSystem.md | 1 + modules/userId/eids.js | 5 ++ test/spec/modules/eids_spec.js | 32 +++++++++++++ test/spec/modules/imuIdSystem_spec.js | 24 ++++++++-- test/spec/modules/userId_spec.js | 25 +++++++++- 6 files changed, 121 insertions(+), 32 deletions(-) diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js index 72e81d243a3..24b11dc1667 100644 --- a/modules/imuIdSystem.js +++ b/modules/imuIdSystem.js @@ -13,21 +13,12 @@ import { getStorageManager } from '../src/storageManager.js'; export const storage = getStorageManager(); export const storageKey = '__im_uid'; +export const storagePpKey = '__im_ppid'; export const cookieKey = '_im_vid'; -export const apiUrl = 'https://audiencedata.im-apps.net/imuid/get'; +export const apiDomain = 'sync6.im-apps.net'; const storageMaxAge = 1800000; // 30 minites (30 * 60 * 1000) const cookiesMaxAge = 97200000000; // 37 months ((365 * 3 + 30) * 24 * 60 * 60 * 1000) -export function setImDataInLocalStorage(value) { - storage.setDataInLocalStorage(storageKey, value); - storage.setDataInLocalStorage(`${storageKey}_mt`, new Date(timestamp()).toUTCString()); -} - -export function removeImDataFromLocalStorage() { - storage.removeDataFromLocalStorage(storageKey); - storage.removeDataFromLocalStorage(`${storageKey}_mt`); -} - function setImDataInCookie(value) { storage.setCookie( cookieKey, @@ -37,6 +28,12 @@ function setImDataInCookie(value) { ); } +export function removeImDataFromLocalStorage() { + storage.removeDataFromLocalStorage(storageKey); + storage.removeDataFromLocalStorage(`${storageKey}_mt`); + storage.removeDataFromLocalStorage(storagePpKey); +} + export function getLocalData() { const mt = storage.getDataFromLocalStorage(`${storageKey}_mt`); let expired = true; @@ -45,17 +42,27 @@ export function getLocalData() { } return { id: storage.getDataFromLocalStorage(storageKey), + ppid: storage.getDataFromLocalStorage(storagePpKey), vid: storage.getCookie(cookieKey), expired: expired }; } +export function getApiUrl(cid, url) { + if (url) { + return `${url}?cid=${cid}`; + } + return `https://${apiDomain}/${cid}/pid`; +} + export function apiSuccessProcess(jsonResponse) { if (!jsonResponse) { return; } - if (jsonResponse.uid) { - setImDataInLocalStorage(jsonResponse.uid); + if (jsonResponse.uid && jsonResponse.ppid) { + storage.setDataInLocalStorage(storageKey, jsonResponse.uid); + storage.setDataInLocalStorage(`${storageKey}_mt`, new Date(timestamp()).toUTCString()); + storage.setDataInLocalStorage(storagePpKey, jsonResponse.ppid); if (jsonResponse.vid) { setImDataInCookie(jsonResponse.vid); } @@ -77,7 +84,11 @@ export function getApiCallback(callback) { } } if (callback && responseObj.uid) { - callback(responseObj.uid); + const callbackObj = { + imuid: responseObj.uid, + imppid: responseObj.ppid + }; + callback(callbackObj); } }, error: error => { @@ -95,13 +106,6 @@ export function callImuidApi(apiUrl) { }; } -export function getApiUrl(cid, url) { - if (url) { - return `${url}?cid=${cid}`; - } - return `${apiUrl}?cid=${cid}`; -} - /** @type {Submodule} */ export const imuIdSubmodule = { /** @@ -112,18 +116,21 @@ export const imuIdSubmodule = { /** * decode the stored id value for passing to bid requests * @function - * @returns {{imuid: string} | undefined} + * @returns {{imuid: string, imppid: string} | undefined} */ - decode(id) { - if (id && typeof id === 'string') { - return {imuid: id}; + decode(ids) { + if (ids && typeof ids === 'object') { + return { + imuid: ids.imuid, + imppid: ids.imppid + }; } return undefined; }, /** * @function * @param {SubmoduleConfig} [config] - * @returns {{id: string} | undefined | {callback:function}}} + * @returns {{id:{imuid: string, imppid: string}} | undefined | {callback:function}}} */ getId(config) { const configParams = (config && config.params) || {}; @@ -144,7 +151,12 @@ export const imuIdSubmodule = { if (localData.expired) { callImuidApi(apiUrl)(); } - return {id: localData.id}; + return { + id: { + imuid: localData.id, + imppid: localData.ppid + } + }; } }; diff --git a/modules/imuIdSystem.md b/modules/imuIdSystem.md index 81aa87ba1d4..cda7fa6528d 100644 --- a/modules/imuIdSystem.md +++ b/modules/imuIdSystem.md @@ -16,6 +16,7 @@ The following configuration parameters are available: ```javascript pbjs.setConfig({ userSync: { + ppid: 'ppid.intimatemerger.com', // GAM Publisher Provided id support userIds: [{ name: 'imuid', params: { diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 4c2e3a00107..e0f100ee660 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -277,6 +277,11 @@ export const USER_IDS_CONFIG = { atype: 3 }, + 'imppid': { + source: 'ppid.intimatemerger.com', + atype: 1 + }, + 'imuid': { source: 'intimatemerger.com', atype: 1 diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index e71058824a4..6872145710c 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -467,6 +467,38 @@ describe('eids array generation for known sub-modules', function() { }]); }); }); + + describe('imuid', function() { + it('should return the correct EID schema with imuid', function() { + const userId = { + imuid: 'testimuid' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'intimatemerger.com', + uids: [{ + id: 'testimuid', + atype: 1 + }] + }); + }); + + it('should return the correct EID schema with imppid', function() { + const userId = { + imppid: 'imppid-value-imppid-value-imppid-value' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'ppid.intimatemerger.com', + uids: [{ + id: 'imppid-value-imppid-value-imppid-value', + atype: 1 + }] + }); + }); + }); }); describe('Negative case', function () { diff --git a/test/spec/modules/imuIdSystem_spec.js b/test/spec/modules/imuIdSystem_spec.js index 2934a7c213b..3650302a2ed 100644 --- a/test/spec/modules/imuIdSystem_spec.js +++ b/test/spec/modules/imuIdSystem_spec.js @@ -7,6 +7,7 @@ import { callImuidApi, getApiCallback, storageKey, + storagePpKey, cookieKey, apiUrl } from 'modules/imuIdSystem.js'; @@ -48,12 +49,17 @@ describe('imuId module', function () { describe('getId()', function () { it('should return the uid when it exists in local storages', function () { getLocalStorageStub.withArgs(storageKey).returns('testUid'); + getLocalStorageStub.withArgs(storagePpKey).returns('testPpid'); const id = imuIdSubmodule.getId(configParamTestCase); - expect(id).to.be.deep.equal({id: 'testUid'}); + expect(id).to.be.deep.equal({id: { + imuid: 'testUid', + imppid: 'testPpid' + }}); }); storageTestCasesForEmpty.forEach(testCase => it('should return the callback when it not exists in local storages', function () { getLocalStorageStub.withArgs(storageKey).returns(testCase); + getLocalStorageStub.withArgs(storagePpKey).returns(testCase); const id = imuIdSubmodule.getId(configParamTestCase); expect(id).have.all.keys('callback'); })); @@ -73,7 +79,7 @@ describe('imuId module', function () { describe('getApiUrl()', function () { it('should return default url when cid only', function () { const url = getApiUrl(5126); - expect(url).to.be.equal(`${apiUrl}?cid=5126`); + expect(url).to.be.equal(`https://sync6.im-apps.net/5126/pid`); }); it('should return param url when set url', function () { @@ -84,8 +90,14 @@ describe('imuId module', function () { describe('decode()', function () { it('should return the uid when it exists in local storages', function () { - const id = imuIdSubmodule.decode('testDecode'); - expect(id).to.be.deep.equal({imuid: 'testDecode'}); + const id = imuIdSubmodule.decode({ + imppid: 'imppid-value-imppid-value-imppid-value', + imuid: 'testDecodeImPpid' + }); + expect(id).to.be.deep.equal({ + imppid: 'imppid-value-imppid-value-imppid-value', + imuid: 'testDecodeImPpid' + }); }); it('should return the undefined when decode id is not "string"', function () { @@ -97,11 +109,13 @@ describe('imuId module', function () { describe('getLocalData()', function () { it('always have the same key', function () { getLocalStorageStub.withArgs(storageKey).returns('testid'); + getLocalStorageStub.withArgs(storagePpKey).returns('imppid-value-imppid-value-imppid-value'); getCookieStub.withArgs(cookieKey).returns('testvid'); getLocalStorageStub.withArgs(`${storageKey}_mt`).returns(new Date(utils.timestamp()).toUTCString()); const localData = getLocalData(); expect(localData).to.be.deep.equal({ id: 'testid', + ppid: 'imppid-value-imppid-value-imppid-value', vid: 'testvid', expired: false }); @@ -112,6 +126,7 @@ describe('imuId module', function () { const localData = getLocalData(); expect(localData).to.be.deep.equal({ id: undefined, + ppid: undefined, vid: undefined, expired: true }); @@ -122,6 +137,7 @@ describe('imuId module', function () { it('should return the undefined when success response', function () { const res = apiSuccessProcess({ uid: 'test', + ppid: 'imppid-value-imppid-value-imppid-value', vid: 'test' }); expect(res).to.equal(undefined); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 77ba1396afb..046b3ffd321 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -46,6 +46,7 @@ import {deepintentDpesSubmodule} from 'modules/deepintentDpesIdSystem.js'; import {amxIdSubmodule} from '../../../modules/amxIdSystem.js'; import {kinessoIdSubmodule} from 'modules/kinessoIdSystem.js'; import {adqueryIdSubmodule} from 'modules/adqueryIdSystem.js'; +import {imuIdSubmodule} from 'modules/imuIdSystem.js'; import * as mockGpt from '../integration/faker/googletag.js'; import 'src/prebid.js'; import {hook} from '../../../src/hook.js'; @@ -402,7 +403,7 @@ describe('User ID', function () { it('should set googletag ppid correctly', function () { let adUnits = [getAdUnitMock()]; init(config); - setSubmoduleRegistry([amxIdSubmodule, sharedIdSystemSubmodule, identityLinkSubmodule]); + setSubmoduleRegistry([amxIdSubmodule, sharedIdSystemSubmodule, identityLinkSubmodule, imuIdSubmodule]); config.setConfig({ userSync: { @@ -411,6 +412,7 @@ describe('User ID', function () { { name: 'amxId', value: {'amxId': 'amx-id-value-amx-id-value-amx-id-value'} }, { name: 'pubCommonId', value: {'pubcid': 'pubCommon-id-value-pubCommon-id-value'} }, { name: 'identityLink', value: {'idl_env': 'identityLink-id-value-identityLink-id-value'} }, + { name: 'imuid', value: {'imppid': 'imppid-value-imppid-value-imppid-value'} }, ] } }); @@ -422,6 +424,27 @@ describe('User ID', function () { }); }); + it('should set googletag ppid correctly for imuIdSubmodule', function () { + let adUnits = [getAdUnitMock()]; + init(config); + setSubmoduleRegistry([imuIdSubmodule]); + + config.setConfig({ + userSync: { + ppid: 'ppid.intimatemerger.com', + userIds: [ + { name: 'imuid', value: {'imppid': 'imppid-value-imppid-value-imppid-value'} }, + ] + } + }); + // before ppid should not be set + expect(window.googletag._ppid).to.equal(undefined); + return expectImmediateBidHook(() => {}, {adUnits}).then(() => { + // ppid should have been set without dashes and stuff + expect(window.googletag._ppid).to.equal('imppidvalueimppidvalueimppidvalue'); + }); + }); + it('should log a warning if PPID too big or small', function () { let adUnits = [getAdUnitMock()]; From 8743babfdbe9f30740d8ee524f7fb8cdf000c3de Mon Sep 17 00:00:00 2001 From: Pgb-Criteo <92986445+Pgb-Criteo@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:27:25 +0200 Subject: [PATCH 102/569] Criteo Bid Adapter: Add Coppa support (#8781) * Criteo Bid Adapter: Add support of the COPPA flag * Criteo Bid Adapter: Add support of the COPPA flag --- modules/criteoBidAdapter.js | 6 +++++- test/spec/modules/criteoBidAdapter_spec.js | 25 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 82fe74c458e..be97a709421 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -175,7 +175,8 @@ export const spec = { Object.assign(bidderRequest, { publisherExt: fpd.site?.ext, userExt: fpd.user?.ext, - ceh: config.getConfig('criteo.ceh') + ceh: config.getConfig('criteo.ceh'), + coppa: config.getConfig('coppa') }); // If publisher tag not already loaded try to get it from fast bid @@ -446,6 +447,9 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { url: context.url, ext: bidderRequest.publisherExt, }, + regs: { + coppa: bidderRequest.coppa === true ? 1 : (bidderRequest.coppa === false ? 0 : undefined) + }, slots: bidRequests.map(bidRequest => { networkId = bidRequest.params.networkId || networkId; schain = bidRequest.schain || schain; diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 58f8e1ad871..ea29d43e4c0 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1213,6 +1213,31 @@ describe('The Criteo bidding adapter', function () { } }); }); + + it('should properly build a request when coppa flag is true', function () { + const bidRequests = []; + const bidderRequest = {}; + config.setConfig({ coppa: true }); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.regs.coppa).to.not.be.undefined; + expect(request.data.regs.coppa).to.equal(1); + }); + + it('should properly build a request when coppa flag is false', function () { + const bidRequests = []; + const bidderRequest = {}; + config.setConfig({ coppa: false }); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.regs.coppa).to.not.be.undefined; + expect(request.data.regs.coppa).to.equal(0); + }); + + it('should properly build a request when coppa flag is not defined', function () { + const bidRequests = []; + const bidderRequest = {}; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.regs.coppa).to.be.undefined; + }); }); describe('interpretResponse', function () { From c2036dbd9075e2eddd1ac0aeae64233e61bcd7fa Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 4 Aug 2022 05:28:45 -0700 Subject: [PATCH 103/569] ix Bid Adapter: fix LGTM trailing semi-colon (#8782) --- modules/ixBidAdapter.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 62ffba3d8dc..ce4fdcc2431 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -47,7 +47,7 @@ const PRICE_TO_DOLLAR_FACTOR = { }; const IFRAME_USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; -const IMG_USER_SYNC_URL = 'https://dsum.casalemedia.com/pbusermatch?origin=prebid' +const IMG_USER_SYNC_URL = 'https://dsum.casalemedia.com/pbusermatch?origin=prebid'; export const ERROR_CODES = { BID_SIZE_INVALID_FORMAT: 1, BID_SIZE_NOT_INCLUDED: 2, @@ -309,7 +309,7 @@ function bidToNativeImp(bid) { const imp = bidToImp(bid); const nativeAdUnitRef = deepAccess(bid, 'mediaTypes.native'); - const assets = [] + const assets = []; // Convert all native assets to imp object for (const [adUnitProperty, adUnitValues] of Object.entries(nativeAdUnitRef)) { @@ -350,7 +350,7 @@ function bidToNativeImp(bid) { methods: [1, 2] }], privacy: 1 - } + }; imp.native = { request: JSON.stringify(request), @@ -536,9 +536,9 @@ function parseBid(rawBid, currency, bidRequest) { bid.creativeId = rawBid.hasOwnProperty('crid') ? rawBid.crid : '-'; if (rawBid.mtype == MEDIA_TYPES.Video) { - bid.vastXml = rawBid.adm + bid.vastXml = rawBid.adm; } else if (rawBid.ext && rawBid.ext.vasturl) { - bid.vastUrl = rawBid.ext.vasturl + bid.vastUrl = rawBid.ext.vasturl; } let parsedAdm = null; @@ -559,7 +559,7 @@ function parseBid(rawBid, currency, bidRequest) { bid.mediaTypes = bidRequest.mediaTypes; bid.ttl = isValidExpiry ? rawBid.exp : VIDEO_TIME_TO_LIVE; } else if (parsedAdm && parsedAdm.native) { - bid.native = interpretNativeAdm(parsedAdm.native) + bid.native = interpretNativeAdm(parsedAdm.native); bid.width = rawBid.w ? rawBid.w : 1; bid.height = rawBid.h ? rawBid.h : 1; bid.mediaType = NATIVE; @@ -889,7 +889,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } if (tmax) { - r.ext.ixdiag.tmax = tmax + r.ext.ixdiag.tmax = tmax; } if (config.getConfig('userSync')) { @@ -943,7 +943,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { if (gdprConsent.hasOwnProperty('addtlConsent') && gdprConsent.addtlConsent) { r.user.ext.consented_providers_settings = { consented_providers: gdprConsent.addtlConsent - } + }; } } } @@ -1061,7 +1061,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { const divId = impressions[transactionIds[adUnitIndex]].divId; if (!gpid && dfpAdUnitCode && divId) { - gpid = `${dfpAdUnitCode}#${divId}` + gpid = `${dfpAdUnitCode}#${divId}`; } if (impressionObjects.length && BANNER in impressionObjects[0]) { const { id, banner: { topframe } } = impressionObjects[0]; @@ -1071,7 +1071,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { topframe, format: impressionObjects.map(({ banner: { w, h }, ext }) => ({ w, h, ext })) }, - } + }; if (dfpAdUnitCode || gpid || tid) { _bannerImpression.ext = {}; @@ -1139,9 +1139,9 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } // add identifiers info to ixDiag - const pbaAdSlot = impressions[transactionIds[adUnitIndex]].pbadslot - const tagId = impressions[transactionIds[adUnitIndex]].tagId - const adUnitCode = impressions[transactionIds[adUnitIndex]].adUnitCode + const pbaAdSlot = impressions[transactionIds[adUnitIndex]].pbadslot; + const tagId = impressions[transactionIds[adUnitIndex]].tagId; + const adUnitCode = impressions[transactionIds[adUnitIndex]].adUnitCode; if (pbaAdSlot || tagId || adUnitCode || divId) { const clonedRObject = deepClone(r); const requestSize = `${baseUrl}${parseQueryStringParameters({ ...payload, r: JSON.stringify(clonedRObject) })}`.length; @@ -1203,7 +1203,7 @@ function _getUserIds(bidRequest) { function buildIXDiag(validBidRequests) { var adUnitMap = validBidRequests .map(bidRequest => bidRequest.transactionId) - .filter((value, index, arr) => arr.indexOf(value) === index) + .filter((value, index, arr) => arr.indexOf(value) === index); var ixdiag = { mfu: 0, @@ -1355,7 +1355,7 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { // Create IX imps from params.size if (bannerSizeDefined) { if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { - bannerImps[validBidRequest.transactionId].ixImps = [] + bannerImps[validBidRequest.transactionId].ixImps = []; } bannerImps[validBidRequest.transactionId].ixImps.push(imp); } From 67cb83ca7121f5a03d41024822799389c5d878ee Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 4 Aug 2022 10:53:02 -0400 Subject: [PATCH 104/569] Various files: clean up unneeded code (#8778) * Update example2.js * Update example.js * Update config.js * Update freewheel-sspBidAdapter.js * Update browsiRtdProvider.js * Update consentManagementUsp.js * Update synacormediaBidAdapter.js * Update trustpidSystem.js * Update tappxBidAdapter.js * Update nobidBidAdapter.js * Update visxBidAdapter.js * Update remote.html * Update prebidServer_example.html * Update openxBidAdapter.js * Update fintezaAnalyticsAdapter.js * Update postbid-config.js * Update postbid-config.js * Update fintezaAnalyticsAdapter.js * Update 1plusXRtdProviderExample.html * Update prebidServer_example.html * Update blueconicRtdProvider_example.html * Update hadronRtdProvider_example.html * Update airgridRtdProvider_example.html * Update userId_example.html * Update inskin_example.html * Update serverbidServer_example.html * Update postbid-config.js --- integrationExamples/gpt/1plusXRtdProviderExample.html | 6 ++---- .../gpt/airgridRtdProvider_example.html | 5 +---- integrationExamples/gpt/amp/remote.html | 2 +- .../gpt/blueconicRtdProvider_example.html | 6 ++---- integrationExamples/gpt/hadronRtdProvider_example.html | 4 +--- integrationExamples/gpt/inskin_example.html | 4 +--- integrationExamples/gpt/prebidServer_example.html | 4 +--- integrationExamples/gpt/serverbidServer_example.html | 4 +--- integrationExamples/gpt/userId_example.html | 4 +--- integrationExamples/postbid/oas/postbid-config.js | 10 +++++----- .../analyticsAdapter/examples/libraries/example.js | 4 ++-- .../analyticsAdapter/examples/libraries/example2.js | 4 ++-- modules/browsiRtdProvider.js | 4 ++-- modules/consentManagementUsp.js | 2 +- modules/fintezaAnalyticsAdapter.js | 2 +- modules/freewheel-sspBidAdapter.js | 2 +- modules/nobidBidAdapter.js | 2 +- modules/openxBidAdapter.js | 2 -- modules/synacormediaBidAdapter.js | 2 +- modules/tappxBidAdapter.js | 4 ++-- modules/trustpidSystem.js | 2 +- modules/visxBidAdapter.js | 2 +- src/config.js | 2 +- 23 files changed, 32 insertions(+), 51 deletions(-) diff --git a/integrationExamples/gpt/1plusXRtdProviderExample.html b/integrationExamples/gpt/1plusXRtdProviderExample.html index 2eb75063df1..b2c7a0037ff 100644 --- a/integrationExamples/gpt/1plusXRtdProviderExample.html +++ b/integrationExamples/gpt/1plusXRtdProviderExample.html @@ -82,9 +82,7 @@ var gads = document.createElement('script'); gads.async = true; gads.type = 'text/javascript'; - var useSSL = 'https:' == document.location.protocol; - gads.src = (useSSL ? 'https:' : 'http:') + - '//securepubads.g.doubleclick.net/tag/js/gpt.js'; + gads.src = 'https://securepubads.g.doubleclick.net/tag/js/gpt.js'; var node = document.getElementsByTagName('script')[0]; node.parentNode.insertBefore(gads, node); })(); @@ -109,4 +107,4 @@

1plusX RTD Module for Prebid

- \ No newline at end of file + diff --git a/integrationExamples/gpt/airgridRtdProvider_example.html b/integrationExamples/gpt/airgridRtdProvider_example.html index a8fd989f682..657eae8f481 100644 --- a/integrationExamples/gpt/airgridRtdProvider_example.html +++ b/integrationExamples/gpt/airgridRtdProvider_example.html @@ -108,10 +108,7 @@ var gads = document.createElement("script"); gads.async = true; gads.type = "text/javascript"; - var useSSL = "https:" == document.location.protocol; - gads.src = - (useSSL ? "https:" : "http:") + - "//www.googletagservices.com/tag/js/gpt.js"; + gads.src = 'https://securepubads.g.doubleclick.net/tag/js/gpt.js'; var node = document.getElementsByTagName("script")[0]; node.parentNode.insertBefore(gads, node); })(); diff --git a/integrationExamples/gpt/amp/remote.html b/integrationExamples/gpt/amp/remote.html index 785d854766f..4ee2cdcb2f6 100644 --- a/integrationExamples/gpt/amp/remote.html +++ b/integrationExamples/gpt/amp/remote.html @@ -33,7 +33,7 @@ // load Prebid.js (function () { - var d = document, pbs = d.createElement("script"), pro = d.location.protocal; + var d = document, pbs = d.createElement("script"); pbs.type = "text/javascript"; pbs.src = prebidSrc; var target = document.getElementsByTagName("head")[0]; diff --git a/integrationExamples/gpt/blueconicRtdProvider_example.html b/integrationExamples/gpt/blueconicRtdProvider_example.html index e51a54b3be6..598a7569d8e 100644 --- a/integrationExamples/gpt/blueconicRtdProvider_example.html +++ b/integrationExamples/gpt/blueconicRtdProvider_example.html @@ -30,7 +30,7 @@ var pbjs = pbjs || {}; pbjs.que = pbjs.que || []; - + - \ No newline at end of file + diff --git a/libraries/analyticsAdapter/examples/libraries/example.js b/libraries/analyticsAdapter/examples/libraries/example.js index 0d758fd5513..f2bfd612193 100644 --- a/libraries/analyticsAdapter/examples/libraries/example.js +++ b/libraries/analyticsAdapter/examples/libraries/example.js @@ -31,8 +31,8 @@ events.init(); // overwrite example object and handle 'on' callbacks window[window.ExampleAnalyticsGlobalObject] = example = utils.errorless(function() { if (arguments[0] && arguments[0] === 'on') { - var eventName = arguments[1] && arguments[1]; - var args = arguments[2] && arguments[2]; + var eventName = arguments[1]; + var args = arguments[2]; if (eventName && args) { if (eventName === 'bidAdjustment') { pbjsHandlers.onBidAdjustment.apply(this, [args]); diff --git a/libraries/analyticsAdapter/examples/libraries/example2.js b/libraries/analyticsAdapter/examples/libraries/example2.js index 68e814b1417..9a7106a48e0 100644 --- a/libraries/analyticsAdapter/examples/libraries/example2.js +++ b/libraries/analyticsAdapter/examples/libraries/example2.js @@ -31,8 +31,8 @@ events.init(); // overwrite example object and handle 'on' callbacks window[window.ExampleAnalyticsGlobalObject2] = example = utils.errorless(function() { if (arguments[0] && arguments[0] === 'on') { - var eventName = arguments[1] && arguments[1]; - var args = arguments[2] && arguments[2]; + var eventName = arguments[1]; + var args = arguments[2]; if (eventName && args) { if (eventName === 'bidAdjustment') { pbjsHandlers.onBidAdjustment.apply(this, [args]); diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 15f2d58010d..ffc946cbb33 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -93,7 +93,7 @@ export function collectData() { function waitForData(callback) { if (_browsiData) { _dataReadyCallback = null; - callback(_browsiData); + callback(); } else { _dataReadyCallback = callback; } @@ -102,7 +102,7 @@ function waitForData(callback) { export function setData(data) { _browsiData = data; if (isFn(_dataReadyCallback)) { - _dataReadyCallback(_browsiData); + _dataReadyCallback(); _dataReadyCallback = null; } } diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index 0224d6ef2c0..cde32a2f1c0 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -44,7 +44,7 @@ function lookupUspConsent({onSuccess, onError}) { let uspapiFrame; let uspapiFunction; - while (!uspapiFrame) { + while (true) { try { if (typeof f.__uspapi === 'function') { uspapiFunction = f.__uspapi; diff --git a/modules/fintezaAnalyticsAdapter.js b/modules/fintezaAnalyticsAdapter.js index 7b1b2f69364..88c5f85f15d 100644 --- a/modules/fintezaAnalyticsAdapter.js +++ b/modules/fintezaAnalyticsAdapter.js @@ -181,7 +181,7 @@ function initSession() { !checkSessionByExpires() || !checkSessionByReferer() || !checkSessionByDay()) { - sessionId = '' + timestamp + getRandAsStr(SESSION_RAND_PART); + sessionId = '' + timestamp + getRandAsStr(SESSION_RAND_PART); // lgtm [js/insecure-randomness] begin = timestamp; isNew = true; diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index 91453fdcf5d..38973f915b6 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -189,7 +189,7 @@ function formatAdHTML(bid, size) { var libUrl = ''; if (integrationType && integrationType !== 'inbanner') { libUrl = PRIMETIME_URL + getComponentId(bid.params.format) + '.min.js'; - script = getOutstreamScript(bid, size); + script = getOutstreamScript(bid); } else { libUrl = MUSTANG_URL; script = getInBannerScript(bid, size); diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index cfe18301b32..877afb08293 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -30,7 +30,7 @@ function nobidBuildRequests(bids, bidderRequest) { var serializeState = function(divIds, siteId, adunits) { var filterAdUnitsByIds = function(divIds, adUnits) { var filtered = []; - if (!divIds || !divIds.length) { + if (!divIds.length) { filtered = adUnits; } else if (adUnits) { var a = []; diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 7bea38a2de3..251e6fb50e3 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -213,13 +213,11 @@ function getViewportDimensions(isIfr) { } catch (e) { return; } - docEl = tDoc.documentElement; body = tDoc.body; width = tWin.innerWidth || docEl.clientWidth || body.clientWidth; height = tWin.innerHeight || docEl.clientHeight || body.clientHeight; } else { - docEl = tDoc.documentElement; width = tWin.innerWidth || docEl.clientWidth; height = tWin.innerHeight || docEl.clientHeight; } diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index a48c1aaf55b..845d6b4b5fb 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -101,7 +101,7 @@ export const spec = { }); // CCPA - if (bidderRequest && bidderRequest.uspConsent) { + if (bidderRequest.uspConsent) { deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index fd038f3104e..b8f7a5559f8 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -406,9 +406,9 @@ function buildOneRequest(validBidRequests, bidderRequest) { (typeof uuid !== 'undefined' && uuid !== null) && (typeof uuid.source == 'string' && uuid.source !== null) && (typeof uuid.uids[0].id == 'string' && uuid.uids[0].id !== null) - ) + ); - if (typeof user !== 'undefined') { user.ext.eids = eidsArr } + user.ext.eids = eidsArr; }; let regs = {}; diff --git a/modules/trustpidSystem.js b/modules/trustpidSystem.js index 27ca4bf6340..fc2bab19913 100644 --- a/modules/trustpidSystem.js +++ b/modules/trustpidSystem.js @@ -84,7 +84,7 @@ function getTrustpidFromStorage() { logInfo(`${LOG_PREFIX}: Domain acronym found: ${acronym}`); // Domain is correct in both local storage and idGraph, but no acronym is existing for the domain - if (domain && !acronym) { + if (!acronym) { return { trustpid: null, acr: null diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index cec48a83c35..ee4d9708f15 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -375,7 +375,7 @@ function _isAdSlotExists(adUnitCode) { } const gptAdSlot = getGptSlotInfoForAdUnitCode(adUnitCode); - if (gptAdSlot && gptAdSlot.divId && document.getElementById(gptAdSlot.divId)) { + if (gptAdSlot.divId && document.getElementById(gptAdSlot.divId)) { return true; } diff --git a/src/config.js b/src/config.js index a50edc09221..30ab8f35e2c 100644 --- a/src/config.js +++ b/src/config.js @@ -526,7 +526,7 @@ export function newConfig() { } const mergedConfig = Object.keys(obj).reduce((accum, key) => { - const prevConf = _getConfig(key)[key] || {}; + const prevConf = _getConfig()[key] || {}; accum[key] = mergeDeep(prevConf, obj[key]); return accum; }, {}); From 35b49996f057a962f2705205e432d2e238827f69 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Thu, 4 Aug 2022 20:33:03 +0530 Subject: [PATCH 105/569] Prebid Core: Handled edge cases for allowedAlternateBidderCodes functionality (#8760) * Changed net revenue to True * handled edge cases * Empty alternateBidderCodes will reject the bids Co-authored-by: Azhar --- src/adapters/bidderFactory.js | 1 + test/spec/unit/core/bidderFactory_spec.js | 29 +++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index e798c63d753..03103dbdcdd 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -260,6 +260,7 @@ export function newBidder(spec) { let allowAlternateBidderCodes = bidderSettings.get(requestBidder, 'allowAlternateBidderCodes') || false; let alternateBiddersList = bidderSettings.get(requestBidder, 'allowedAlternateBidderCodes'); if (!!responseBidder && !!requestBidder && requestBidder !== responseBidder) { + alternateBiddersList = isArray(alternateBiddersList) ? alternateBiddersList.map(val => val.trim().toLowerCase()).filter(val => !!val).filter(uniques) : alternateBiddersList; if (!allowAlternateBidderCodes || (isArray(alternateBiddersList) && (alternateBiddersList[0] !== '*' && !alternateBiddersList.includes(responseBidder)))) { return true; } diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index c040bba4eec..93cb7bd4449 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -1050,8 +1050,8 @@ describe('validate bid response: ', function () { bids1 = Object.assign({}, bids[0], { - bidderCode: 'validAlternateBidder', - adapterCode: 'knownAdapter1' + bidderCode: 'validalternatebidder', + adapterCode: 'knownadapter1' } ); logWarnSpy = sinon.spy(utils, 'logWarn'); @@ -1126,6 +1126,31 @@ describe('validate bid response: ', function () { expect(logErrorSpy.callCount).to.equal(0); }); + it('should accept the bid, when allowedAlternateBidderCodes is marked as * (with space) and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns([' * ']); + + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(logWarnSpy.callCount).to.equal(0); + expect(logErrorSpy.callCount).to.equal(0); + }); + + it('should not accept the bid, when allowedAlternateBidderCodes is marked as empty array and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns([]); + + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(addBidResponseStub.calledOnce).to.equal(false); + expect(logWarnSpy.callCount).to.equal(1); + }); + it('should accept the bid, when allowedAlternateBidderCodes contains bidder name and allowAlternateBidderCodes flag is true', function () { bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(['validAlternateBidder']); From cdb290c501d626d9020e2c0d5ce03b1edc4785bd Mon Sep 17 00:00:00 2001 From: dzhang-criteo <87757739+dzhang-criteo@users.noreply.github.com> Date: Thu, 4 Aug 2022 19:47:23 +0200 Subject: [PATCH 106/569] Criteo Adapter: remove bid.adId (#8783) --- modules/criteoBidAdapter.js | 3 +- test/spec/modules/criteoBidAdapter_spec.js | 37 ---------------------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index be97a709421..590062c76bd 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, getUniqueIdentifierStr, isArray, logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; +import { deepAccess, isArray, logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; import { loadExternalScript } from '../src/adloader.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; @@ -235,7 +235,6 @@ export const spec = { const bidId = bidRequest.bidId; const bid = { requestId: bidId, - adId: slot.bidId || getUniqueIdentifierStr(), cpm: slot.cpm, currency: slot.currency, netRevenue: true, diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index ea29d43e4c0..19e97ea85bb 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1310,7 +1310,6 @@ describe('The Criteo bidding adapter', function () { const bids = spec.interpretResponse(response, request); expect(bids).to.have.lengthOf(1); expect(bids[0].requestId).to.equal('test-bidId'); - expect(bids[0].adId).to.equal('abc123'); expect(bids[0].cpm).to.equal(1.23); expect(bids[0].ad).to.equal('test-ad'); expect(bids[0].width).to.equal(728); @@ -1344,7 +1343,6 @@ describe('The Criteo bidding adapter', function () { const bids = spec.interpretResponse(response, request); expect(bids).to.have.lengthOf(1); expect(bids[0].requestId).to.equal('test-bidId'); - expect(bids[0].adId).to.equal('abc123'); expect(bids[0].cpm).to.equal(1.23); expect(bids[0].vastUrl).to.equal('http://test-ad'); expect(bids[0].mediaType).to.equal(VIDEO); @@ -1401,7 +1399,6 @@ describe('The Criteo bidding adapter', function () { const bids = spec.interpretResponse(response, request); expect(bids).to.have.lengthOf(1); expect(bids[0].requestId).to.equal('test-bidId'); - expect(bids[0].adId).to.equal('abc123'); expect(bids[0].cpm).to.equal(1.23); expect(bids[0].mediaType).to.equal(NATIVE); }); @@ -1520,40 +1517,6 @@ describe('The Criteo bidding adapter', function () { expect(bids[0].height).to.equal(90); }); - it('should generate unique adIds if none are returned by the endpoint', function () { - const response = { - body: { - slots: [{ - impid: 'test-requestId', - cpm: 1.23, - creative: 'test-ad', - width: 300, - height: 250, - }, { - impid: 'test-requestId', - cpm: 4.56, - creative: 'test-ad', - width: 728, - height: 90, - }], - }, - }; - const request = { - bidRequests: [{ - adUnitCode: 'test-requestId', - bidId: 'test-bidId', - sizes: [[300, 250], [728, 90]], - params: { - networkId: 456, - } - }] - }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(2); - const prebidBids = bids.map(bid => Object.assign(createBid(CONSTANTS.STATUS.GOOD, request.bidRequests[0]), bid)); - expect(prebidBids[0].adId).to.not.equal(prebidBids[1].adId); - }); - [{ hasBidResponseLevelPafData: true, hasBidResponseBidLevelPafData: true, From a93a7c98adf1a3037c759069e817d8916ae60497 Mon Sep 17 00:00:00 2001 From: kapil-tuptewar <91458408+kapil-tuptewar@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:32:23 +0530 Subject: [PATCH 107/569] Sending device.language in iso standard 2 characters (#8789) Co-authored-by: Kapil Tuptewar --- modules/pubmaticBidAdapter.js | 3 +++ test/spec/modules/pubmaticBidAdapter_spec.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 9ab5984860b..42c05f765bc 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1112,6 +1112,9 @@ export const spec = { payload.device = Object.assign(payload.device, config.getConfig('device')); } + // update device.language to ISO-639-1-alpha-2 (2 character language) + payload.device.language = payload.device.language && payload.device.language.split('-')[0]; + // passing transactionId in source.tid deepSetValue(payload, 'source.tid', conf.transactionId); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index e8bedc851c7..6e65e725024 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1141,7 +1141,7 @@ describe('PubMatic adapter', function () { expect(data.device.dnt).to.equal((navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0); expect(data.device.h).to.equal(screen.height); expect(data.device.w).to.equal(screen.width); - expect(data.device.language).to.equal(navigator.language); + expect(data.device.language).to.equal(navigator.language.split('-')[0]); expect(data.device.newkey).to.equal('new-device-data');// additional data from config sandbox.restore(); }); From 81a9e50cc2c666630b372363d795f39b241dd9b5 Mon Sep 17 00:00:00 2001 From: Snigel <108489367+snigelweb@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:04:41 +0200 Subject: [PATCH 108/569] Snigel Bid Adapter: initial adapter release (#8723) * add snigel prebid adapter, docs and tests * fix small oversight * improve user sync behavior * implement PR feedback, add support for schain, floors and user IDs --- modules/snigelBidAdapter.js | 150 +++++++++++ modules/snigelBidAdapter.md | 46 ++++ test/spec/modules/snigelBidAdapter_spec.js | 296 +++++++++++++++++++++ 3 files changed, 492 insertions(+) create mode 100644 modules/snigelBidAdapter.js create mode 100644 modules/snigelBidAdapter.md create mode 100644 test/spec/modules/snigelBidAdapter_spec.js diff --git a/modules/snigelBidAdapter.js b/modules/snigelBidAdapter.js new file mode 100644 index 00000000000..e0ec10c6ed6 --- /dev/null +++ b/modules/snigelBidAdapter.js @@ -0,0 +1,150 @@ +import {config} from '../src/config.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import {deepAccess, isArray, isFn, isPlainObject} from '../src/utils.js'; +import {hasPurpose1Consent} from '../src/utils/gpdr.js'; + +const BIDDER_CODE = 'snigel'; +const GVLID = 1076; +const DEFAULT_URL = 'https://adserv.snigelweb.com/bp/v1/prebid'; +const DEFAULT_TTL = 60; +const DEFAULT_CURRENCIES = ['USD']; +const FLOOR_MATCH_ALL_SIZES = '*'; + +const getConfig = config.getConfig; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function (bidRequest) { + return !!bidRequest.params.placement; + }, + + buildRequests: function (bidRequests, bidderRequest) { + const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); + return { + method: 'POST', + url: getEndpoint(), + data: JSON.stringify({ + id: bidderRequest.bidderRequestId, + cur: getCurrencies(), + test: getTestFlag(), + devw: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, + devh: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, + version: $$PREBID_GLOBAL$$.version, + gdprApplies: gdprApplies, + gdprConsentString: gdprApplies === true ? deepAccess(bidderRequest, 'gdprConsent.consentString') : undefined, + gdprConsentProv: gdprApplies === true ? deepAccess(bidderRequest, 'gdprConsent.addtlConsent') : undefined, + uspConsent: deepAccess(bidderRequest, 'uspConsent'), + coppa: getConfig('coppa'), + eids: deepAccess(bidRequests, '0.userIdAsEids'), + schain: deepAccess(bidRequests, '0.schain'), + page: getPage(bidderRequest), + placements: bidRequests.map((r) => { + return { + uuid: r.bidId, + name: r.params.placement, + sizes: r.sizes, + floor: getPriceFloor(r, BANNER, FLOOR_MATCH_ALL_SIZES), + }; + }), + }), + bidderRequest, + }; + }, + + interpretResponse: function (serverResponse) { + if (!serverResponse.body || !serverResponse.body.bids) { + return []; + } + + return serverResponse.body.bids.map((bid) => { + return { + requestId: bid.uuid, + cpm: bid.price, + creativeId: bid.crid, + currency: serverResponse.body.cur, + width: bid.width, + height: bid.height, + ad: bid.ad, + netRevenue: true, + ttl: bid.ttl || DEFAULT_TTL, + meta: bid.meta, + }; + }); + }, + + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { + const syncUrl = getSyncUrl(responses || []); + if (syncUrl && syncOptions.iframeEnabled && hasSyncConsent(gdprConsent, uspConsent)) { + return [{type: 'iframe', url: getSyncEndpoint(syncUrl, gdprConsent)}]; + } + }, +}; + +registerBidder(spec); + +function getPage(bidderRequest) { + return ( + getConfig(`${BIDDER_CODE}.page`) || deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || window.location.href + ); +} + +function getEndpoint() { + return getConfig(`${BIDDER_CODE}.url`) || DEFAULT_URL; +} + +function getTestFlag() { + return getConfig(`${BIDDER_CODE}.test`) === true; +} + +function getCurrencies() { + const currencyOverrides = getConfig(`${BIDDER_CODE}.cur`); + if (currencyOverrides !== undefined && (!isArray(currencyOverrides) || currencyOverrides.length === 0)) { + throw Error('Currency override must be an array with at least one currency'); + } + return currencyOverrides || DEFAULT_CURRENCIES; +} + +function getFloorCurrency() { + return getConfig(`${BIDDER_CODE}.floorCur`) || getCurrencies()[0]; +} + +function getPriceFloor(bidRequest, mediaType, size) { + if (isFn(bidRequest.getFloor)) { + const cur = getFloorCurrency(); + const floorInfo = bidRequest.getFloor({ + currency: cur, + mediaType: mediaType, + size: size, + }); + if (isPlainObject(floorInfo) && !isNaN(floorInfo.floor)) { + return { + cur: floorInfo.currency || cur, + value: floorInfo.floor, + }; + } + } +} + +function hasSyncConsent(gdprConsent, uspConsent) { + if (gdprConsent?.gdprApplies && !hasPurpose1Consent(gdprConsent)) { + return false; + } else if (uspConsent && uspConsent[1] === 'Y' && uspConsent[2] === 'Y') { + return false; + } else { + return true; + } +} + +function getSyncUrl(responses) { + return getConfig(`${BIDDER_CODE}.syncUrl`) || deepAccess(responses[0], 'body.syncUrl'); +} + +function getSyncEndpoint(url, gdprConsent) { + return `${url}?gdpr=${gdprConsent?.gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent( + gdprConsent?.consentString || '' + )}`; +} diff --git a/modules/snigelBidAdapter.md b/modules/snigelBidAdapter.md new file mode 100644 index 00000000000..a83e133144f --- /dev/null +++ b/modules/snigelBidAdapter.md @@ -0,0 +1,46 @@ +# Overview + +- Module name: Snigel Bid Adapter +- Module type: Bidder Adapter +- Maintainer: devops@snigel.com +- Bidder code: snigel +- Supported media types: Banner + +# Description + +Connects to Snigel demand sources for bids. + +**Note:** This bid adapter requires our ad operation experts to create an optimized setup for the desired placements on your property. +Please reach out to us [through our contact form](https://snigel.com/get-in-touch). We will reply as soon as possible. + +# Parameters + +| Name | Required | Description | Example | +| :--- | :-------- | :---------- | :------ | +| placement | Yes | Placement identifier | top_leaderboard | + +# Test + +```js +var adUnits = [ + { + code: "example", + mediaTypes: { + banner: { + sizes: [ + [970, 90], + [728, 90], + ], + }, + }, + bids: [ + { + bidder: "snigel", + params: { + placement: "prebid_test_placement", + }, + }, + ], + }, +]; +``` diff --git a/test/spec/modules/snigelBidAdapter_spec.js b/test/spec/modules/snigelBidAdapter_spec.js new file mode 100644 index 00000000000..3fc09493f03 --- /dev/null +++ b/test/spec/modules/snigelBidAdapter_spec.js @@ -0,0 +1,296 @@ +import {expect} from 'chai'; +import {spec} from 'modules/snigelBidAdapter.js'; +import {config} from 'src/config.js'; +import {isValid} from 'src/adapters/bidderFactory.js'; + +const BASE_BID_REQUEST = { + adUnitCode: 'top_leaderboard', + bidId: 'bid_test', + sizes: [ + [970, 90], + [728, 90], + ], + bidder: 'snigel', + params: {}, + requestId: 'req_test', + transactionId: 'trans_test', +}; +const makeBidRequest = function (overrides) { + return {...BASE_BID_REQUEST, ...overrides}; +}; + +const BASE_BIDDER_REQUEST = { + bidderRequestId: 'test', + refererInfo: { + canonicalUrl: 'https://localhost', + }, +}; +const makeBidderRequest = function (overrides) { + return {...BASE_BIDDER_REQUEST, ...overrides}; +}; + +const DUMMY_USP_CONSENT = '1YYN'; +const DUMMY_GDPR_CONSENT_STRING = + 'BOSSotLOSSotLAPABAENBc-AAAAgR7_______9______9uz_Gv_v_f__33e8__9v_l_7_-___u_-33d4-_1vX99yfm1-7ftr3tp_86ues2_XqK_9oIiA'; + +describe('snigelBidAdapter', function () { + describe('isBidRequestValid', function () { + it('should return false if no placement provided', function () { + expect(spec.isBidRequestValid(BASE_BID_REQUEST)).to.equal(false); + }); + + it('should return true if placement provided', function () { + const bidRequest = makeBidRequest({params: {placement: 'top_leaderboard'}}); + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + afterEach(function () { + config.resetConfig(); + }); + + it('should build a single request for every impression and its placement', function () { + const bidderRequest = Object.assign({}, BASE_BIDDER_REQUEST); + const bidRequests = [ + makeBidRequest({bidId: 'a', params: {placement: 'top_leaderboard'}}), + makeBidRequest({bidId: 'b', params: {placement: 'bottom_leaderboard'}}), + ]; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request).to.be.an('object'); + expect(request).to.have.property('url').and.to.equal('https://adserv.snigelweb.com/bp/v1/prebid'); + expect(request).to.have.property('method').and.to.equal('POST'); + + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('id').and.to.equal('test'); + expect(data).to.have.property('cur').and.to.deep.equal(['USD']); + expect(data).to.have.property('test').and.to.equal(false); + expect(data).to.have.property('page').and.to.equal('https://localhost'); + expect(data).to.have.property('placements'); + expect(data.placements.length).to.equal(2); + expect(data.placements[0].uuid).to.equal('a'); + expect(data.placements[0].name).to.equal('top_leaderboard'); + expect(data.placements[1].uuid).to.equal('b'); + expect(data.placements[1].name).to.equal('bottom_leaderboard'); + }); + + it('should forward GDPR flag and GDPR consent string if enabled', function () { + const bidderRequest = makeBidderRequest({ + gdprConsent: { + gdprApplies: true, + consentString: DUMMY_GDPR_CONSENT_STRING, + }, + }); + + const request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('gdprApplies').and.to.equal(true); + expect(data).to.have.property('gdprConsentString').and.to.equal(DUMMY_GDPR_CONSENT_STRING); + }); + + it('should forward GDPR flag and no GDPR consent string if disabled', function () { + const bidderRequest = makeBidderRequest({ + gdprConsent: { + gdprApplies: false, + consentString: DUMMY_GDPR_CONSENT_STRING, + }, + }); + + const request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('gdprApplies').and.to.equal(false); + expect(data).to.not.have.property('gdprConsentString'); + }); + + it('should forward USP consent if set', function () { + const bidderRequest = makeBidderRequest({ + uspConsent: DUMMY_USP_CONSENT, + }); + + const request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('uspConsent').and.to.equal(DUMMY_USP_CONSENT); + }); + + it('should forward whether or not COPPA applies', function () { + config.setConfig({ + 'coppa': true, + }); + + const request = spec.buildRequests([], BASE_BIDDER_REQUEST); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('coppa').and.to.equal(true); + }); + }); + + describe('interpretResponse', function () { + it('should not return any bids if the request failed', function () { + expect(spec.interpretResponse({}, {})).to.be.empty; + expect(spec.interpretResponse({body: 'Some error message'}, {})).to.be.empty; + }); + + it('should not return any bids if the request did not return any bids either', function () { + expect(spec.interpretResponse({body: {bids: []}})).to.be.empty; + }); + + it('should return valid bids with additional meta information', function () { + const serverResponse = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + bids: [ + { + uuid: BASE_BID_REQUEST.bidId, + price: 0.0575, + ad: '

Test Ad

', + width: 728, + height: 90, + crid: 'test', + meta: { + advertiserDomains: ['addomain.com'], + }, + }, + ], + }, + }; + + const bids = spec.interpretResponse(serverResponse, {}); + expect(bids.length).to.equal(1); + const bid = bids[0]; + expect(isValid(BASE_BID_REQUEST.adUnitCode, bid)).to.be.true; + expect(bid).to.have.property('meta'); + expect(bid.meta).to.have.property('advertiserDomains'); + expect(bid.meta.advertiserDomains).to.be.an('array'); + expect(bid.meta.advertiserDomains.length).to.equal(1); + expect(bid.meta.advertiserDomains[0]).to.equal('addomain.com'); + }); + }); + + describe('getUserSyncs', function () { + it('should not return any user syncs if sync url does not exist in response', function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: true, + }; + const gdprConsent = { + gdprApplies: false, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.undefined; + }); + + it('should not return any user syncs if publisher disabled iframe-based sync', function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + syncUrl: 'https://somesyncurl', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: false, + }; + const gdprConsent = { + gdprApplies: false, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.undefined; + }); + + it('should not return any user syncs if GDPR applies and the user did not consent to purpose one', function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + syncUrl: 'https://somesyncurl', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: true, + }; + const gdprConsent = { + gdprApplies: true, + vendorData: { + purpose: { + consents: {1: false}, + }, + }, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.undefined; + }); + + it("should return an iframe specific to the publisher's property if all conditions are met", function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + syncUrl: 'https://somesyncurl', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: true, + }; + const gdprConsent = { + gdprApplies: false, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.an('array').and.of.length(1); + const sync = syncs[0]; + expect(sync).to.have.property('type'); + expect(sync.type).to.equal('iframe'); + expect(sync).to.have.property('url'); + expect(sync.url).to.equal('https://somesyncurl?gdpr=0&gdpr_consent='); + }); + + it('should pass GDPR applicability and consent string as query parameters', function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + syncUrl: 'https://somesyncurl', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: true, + }; + const gdprConsent = { + gdprApplies: true, + consentString: DUMMY_GDPR_CONSENT_STRING, + vendorData: { + purpose: { + consents: {1: true}, + }, + }, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.an('array').and.of.length(1); + const sync = syncs[0]; + expect(sync).to.have.property('type'); + expect(sync.type).to.equal('iframe'); + expect(sync).to.have.property('url'); + expect(sync.url).to.equal(`https://somesyncurl?gdpr=1&gdpr_consent=${DUMMY_GDPR_CONSENT_STRING}`); + }); + }); +}); From dd3267dcbef849ea9ddb509967f95711e9f585d0 Mon Sep 17 00:00:00 2001 From: Brian Schmidt Date: Sun, 7 Aug 2022 07:59:29 -0700 Subject: [PATCH 109/569] openxOrtb support 204 response, small bug fix (#8796) --- modules/openxOrtbBidAdapter.js | 6 +++- test/spec/modules/openxOrtbBidAdapter_spec.js | 33 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/modules/openxOrtbBidAdapter.js b/modules/openxOrtbBidAdapter.js index a7a8d53a624..bbe0e827b8e 100644 --- a/modules/openxOrtbBidAdapter.js +++ b/modules/openxOrtbBidAdapter.js @@ -252,6 +252,10 @@ function getFloor(bid, mediaType) { } function interpretResponse(resp, req) { + if (!resp.body) { + resp.body = {nbr: 0}; + } + // pass these from request to the responses for use in userSync if (req.data.ext) { if (req.data.ext.delDomain) { @@ -301,7 +305,7 @@ function interpretResponse(resp, req) { } if (respBody.ext && respBody.ext.paf) { - response.meta.paf = respBody.ext.paf; + response.meta.paf = Object.assign({}, respBody.ext.paf); response.meta.paf.content_id = utils.deepAccess(bid, 'ext.paf.content_id'); } diff --git a/test/spec/modules/openxOrtbBidAdapter_spec.js b/test/spec/modules/openxOrtbBidAdapter_spec.js index b3f63d9c23a..3fe9b627d04 100644 --- a/test/spec/modules/openxOrtbBidAdapter_spec.js +++ b/test/spec/modules/openxOrtbBidAdapter_spec.js @@ -952,7 +952,7 @@ describe('OpenxRtbAdapter', function () { let bidResponse; let bid; - context('when there is no response', function () { + context('when there is an nbr response', function () { let bids; beforeEach(function () { bidRequestConfigs = [{ @@ -983,6 +983,37 @@ describe('OpenxRtbAdapter', function () { }); }); + context('when there is no response', function () { + let bids; + beforeEach(function () { + bidRequestConfigs = [{ + bidder: 'openx', + params: { + unit: '12345678', + delDomain: 'test-del-domain' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + }, + }, + bidId: 'test-bid-id', + bidderRequestId: 'test-bidder-request-id', + auctionId: 'test-auction-id' + }]; + + bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + + bidResponse = ''; // Unknown error + bids = spec.interpretResponse({body: bidResponse}, bidRequest); + }); + + it('should not return any bids', function () { + expect(bids.length).to.equal(0); + }); + }); + context('when there is a response, the common response properties', function () { beforeEach(function () { bidRequestConfigs = [{ From d7e7aa142598887a3b74a5e458f3dbaa290ef7a2 Mon Sep 17 00:00:00 2001 From: Alexey Stoletny Date: Mon, 8 Aug 2022 09:05:49 -0500 Subject: [PATCH 110/569] CleanIO RTD Module: support billable event (#8750) * clean.io RTD Module: send billable events * Added unit test for billable events * attempt to trigger circleci * Fixed some analytics test to expect Clean.io billableEvents too. Co-authored-by: mkikot-sigma Co-authored-by: Patrick McCann Co-authored-by: eugen-tikhonov <31205179+eugen-tikhonov@users.noreply.github.com> Co-authored-by: yevhen.tykhonov --- modules/cleanioRtdProvider.js | 29 +++++++++++++++++++ test/spec/modules/cleanioRtdProvider_spec.js | 23 +++++++++++++++ .../modules/concertAnalyticsAdapter_spec.js | 3 +- .../modules/eplanningAnalyticsAdapter_spec.js | 4 +-- .../modules/fintezaAnalyticsAdapter_spec.js | 3 +- .../modules/invisiblyAnalyticsAdapter_spec.js | 11 +++++-- .../modules/konduitAnalyticsAdapter_spec.js | 3 +- .../modules/optimonAnalyticsAdapter_spec.js | 3 +- .../modules/pianoDmpAnalyticsAdapter_spec.js | 4 +++ .../prebidmanagerAnalyticsAdapter_spec.js | 3 +- .../modules/pubperfAnalyticsAdapter_spec.js | 3 +- .../modules/pubstackAnalyticsAdapter_spec.js | 3 +- .../modules/pubwiseAnalyticsAdapter_spec.js | 6 ++-- .../modules/sigmoidAnalyticsAdapter_spec.js | 2 +- .../modules/sovrnAnalyticsAdapter_spec.js | 3 +- .../modules/yieldoneAnalyticsAdapter_spec.js | 6 +++- 16 files changed, 91 insertions(+), 18 deletions(-) diff --git a/modules/cleanioRtdProvider.js b/modules/cleanioRtdProvider.js index b9fdcef768e..a6ac3fc1317 100644 --- a/modules/cleanioRtdProvider.js +++ b/modules/cleanioRtdProvider.js @@ -8,6 +8,8 @@ import { submodule } from '../src/hook.js'; import { logError, generateUUID, insertElement } from '../src/utils.js'; +import * as events from '../src/events.js'; +import CONSTANTS from '../src/constants.json'; // ============================ MODULE STATE =============================== @@ -147,6 +149,26 @@ function readConfig(config) { } } +/** + * The function to be called upon module init + * Defined as a variable to be able to reset it naturally + */ +let startBillableEvents = function() { + // Upon clean.io submodule initialization, every winner bid is considered to be protected + // and therefore, subjected to billing + events.on(CONSTANTS.EVENTS.BID_WON, winnerBidResponse => { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: 'clean.io', + billingId: generateUUID(), + type: 'impression', + // TODO: if absolutely crucial, winnerBidResponse may be used + // to track down auctionId and transactionId + // However, those seem to be of importance for Demand Managers, + // while these billable events are for publishers + }); + }); +} + // ============================ MODULE REGISTRATION =============================== /** @@ -160,6 +182,13 @@ function beforeInit() { try { readConfig(config); onModuleInit(); + + // Subscribing once to ensure no duplicate events + // in case module initialization code runs multiple times + // This should have been a part of submodule definition, but well... + // The assumption here is that in production init() will be called exactly once + startBillableEvents(); + startBillableEvents = () => {}; return true; } catch (err) { if (err instanceof ConfigError) { diff --git a/test/spec/modules/cleanioRtdProvider_spec.js b/test/spec/modules/cleanioRtdProvider_spec.js index 47c4b1b4961..8453b633906 100644 --- a/test/spec/modules/cleanioRtdProvider_spec.js +++ b/test/spec/modules/cleanioRtdProvider_spec.js @@ -1,5 +1,7 @@ import * as utils from '../../../src/utils.js'; import * as hook from '../../../src/hook.js' +import * as events from '../../../src/events.js'; +import CONSTANTS from '../../../src/constants.json'; import { __TEST__ } from '../../../modules/cleanioRtdProvider.js'; @@ -184,5 +186,26 @@ describe('clean.io RTD module', function () { onBidResponseEvent(fakeBidResponse3, {}, {}); ensurePrependToBidResponse(fakeBidResponse3); }); + + it('should send billable event per bid won event', function () { + const { init } = getModule(); + expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'full' } }, {})).to.equal(true); + + const eventCounter = { registerCleanioBillingEvent: function() {} }; + sinon.spy(eventCounter, 'registerCleanioBillingEvent'); + + events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, (evt) => { + if (evt.vendor === 'clean.io') { + eventCounter.registerCleanioBillingEvent() + } + }); + + events.emit(CONSTANTS.EVENTS.BID_WON, {}); + events.emit(CONSTANTS.EVENTS.BID_WON, {}); + events.emit(CONSTANTS.EVENTS.BID_WON, {}); + events.emit(CONSTANTS.EVENTS.BID_WON, {}); + + sinon.assert.callCount(eventCounter.registerCleanioBillingEvent, 4); + }); }); }); diff --git a/test/spec/modules/concertAnalyticsAdapter_spec.js b/test/spec/modules/concertAnalyticsAdapter_spec.js index b0aad2f3156..d130aea6043 100644 --- a/test/spec/modules/concertAnalyticsAdapter_spec.js +++ b/test/spec/modules/concertAnalyticsAdapter_spec.js @@ -48,7 +48,8 @@ describe('ConcertAnalyticsAdapter', function() { sandbox.spy(concertAnalytics, 'track'); fireBidEvents(events); - sandbox.assert.callCount(concertAnalytics.track, 5); + // 5 Concert events + 1 Clean.io event + sandbox.assert.callCount(concertAnalytics.track, 6); }); it('should report data for BID_RESPONSE, BID_WON events', function() { diff --git a/test/spec/modules/eplanningAnalyticsAdapter_spec.js b/test/spec/modules/eplanningAnalyticsAdapter_spec.js index 872518f2f27..bb9e6c4fb86 100644 --- a/test/spec/modules/eplanningAnalyticsAdapter_spec.js +++ b/test/spec/modules/eplanningAnalyticsAdapter_spec.js @@ -153,8 +153,8 @@ describe('eplanning analytics adapter', function () { // Step 10 check that the host to send the ajax request is configurable via options expect(eplAnalyticsAdapter.context.host).to.equal(initOptions.host); - // Step 11 verify that we received 6 events - sinon.assert.callCount(eplAnalyticsAdapter.track, 6); + // Step 11 verify that we received 7 events (6 E-Planning events + 1 Clean.io event) + sinon.assert.callCount(eplAnalyticsAdapter.track, 7); }); }); }); diff --git a/test/spec/modules/fintezaAnalyticsAdapter_spec.js b/test/spec/modules/fintezaAnalyticsAdapter_spec.js index 76f77505105..cddffc63554 100644 --- a/test/spec/modules/fintezaAnalyticsAdapter_spec.js +++ b/test/spec/modules/fintezaAnalyticsAdapter_spec.js @@ -189,7 +189,8 @@ describe('finteza analytics adapter', function () { expect(url.search.value).to.equal(String(cpm)); expect(url.search.unit).to.equal('usd'); - sinon.assert.callCount(fntzAnalyticsAdapter.track, 1); + // 1 Finteza event + 1 Clean.io event + sinon.assert.callCount(fntzAnalyticsAdapter.track, 2); }); }); diff --git a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js index e13b16661b0..d97b0925713 100644 --- a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js +++ b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js @@ -202,7 +202,9 @@ describe('Invisibly Analytics Adapter test suite', function () { events.emit(constants.EVENTS.BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE); events.emit(constants.EVENTS.BID_WON, MOCK.BID_WON); - sinon.assert.callCount(invisiblyAdapter.track, 5); + + // 5 Invisibly events + 1 Clean.io event + sinon.assert.callCount(invisiblyAdapter.track, 6); }); it('should not catch events triggered without invisibly account config', function () { @@ -380,7 +382,9 @@ describe('Invisibly Analytics Adapter test suite', function () { expect(invisiblyEvents.event_data.pageViewId).to.exist; expect(invisiblyEvents.event_data.ver).to.equal(1); expect(invisiblyEvents.event_type).to.equal('PREBID_bidWon'); - sinon.assert.callCount(invisiblyAdapter.track, 1); + + // 1 Invisibly event + 1 Clean.io event + sinon.assert.callCount(invisiblyAdapter.track, 2); }); // spec for bidder done event @@ -551,7 +555,8 @@ describe('Invisibly Analytics Adapter test suite', function () { events.emit(constants.EVENTS.ADD_AD_UNITS, MOCK.ADD_AD_UNITS); events.emit(constants.EVENTS.AD_RENDER_FAILED, MOCK.AD_RENDER_FAILED); - sinon.assert.callCount(invisiblyAdapter.track, 13); + // 13 Invisibly events + 1 Clean.io event + sinon.assert.callCount(invisiblyAdapter.track, 14); }); }); diff --git a/test/spec/modules/konduitAnalyticsAdapter_spec.js b/test/spec/modules/konduitAnalyticsAdapter_spec.js index ac557d27f90..1612138cde4 100644 --- a/test/spec/modules/konduitAnalyticsAdapter_spec.js +++ b/test/spec/modules/konduitAnalyticsAdapter_spec.js @@ -121,6 +121,7 @@ describe(`Konduit Analytics Adapter`, () => { expect(requestBody.konduitId).to.be.equal(konduitId); expect(requestBody.prebidVersion).to.be.equal('$prebid.version$'); expect(requestBody.environment).to.be.an('object'); - sinon.assert.callCount(konduitAnalyticsAdapter.track, 6); + // 6 Konduit events + 1 Clean.io event + sinon.assert.callCount(konduitAnalyticsAdapter.track, 7); }); }); diff --git a/test/spec/modules/optimonAnalyticsAdapter_spec.js b/test/spec/modules/optimonAnalyticsAdapter_spec.js index c50bfcb170f..406e9cb75c4 100644 --- a/test/spec/modules/optimonAnalyticsAdapter_spec.js +++ b/test/spec/modules/optimonAnalyticsAdapter_spec.js @@ -35,6 +35,7 @@ describe('Optimon Analytics Adapter', () => { events.emit(constants.EVENTS.BID_TIMEOUT, optmn_arguments) events.emit(constants.EVENTS.BID_WON, optmn_arguments) - expect(optmn_queue.length).to.eql(3); + // 3 Optimon events + 1 Clean.io event + expect(optmn_queue.length).to.eql(4); }); }); diff --git a/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js b/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js index 240e90c3c32..49b048f1fe6 100644 --- a/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js +++ b/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js @@ -52,6 +52,10 @@ describe('Piano DMP Analytics Adapter', () => { // Then const callQueue = (window.cX || {}).callQueue; + const billableEventIndex = callQueue.findIndex(([, params]) => params.eventType === constants.EVENTS.BILLABLE_EVENT); + if (billableEventIndex > -1) { + callQueue.splice(billableEventIndex, 1); + } expect(callQueue).to.be.an('array'); expect(callQueue.length).to.equal(testEvents.length); diff --git a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js index e504eced868..2e1bf4df062 100644 --- a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js +++ b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js @@ -102,7 +102,8 @@ describe('Prebid Manager Analytics Adapter', function () { events.emit(constants.EVENTS.AUCTION_END, {}); events.emit(constants.EVENTS.BID_TIMEOUT, {}); - sinon.assert.callCount(prebidmanagerAnalytics.track, 6); + // 6 Prebid Manager events + 1 Clean.io event + sinon.assert.callCount(prebidmanagerAnalytics.track, 7); }); }); diff --git a/test/spec/modules/pubperfAnalyticsAdapter_spec.js b/test/spec/modules/pubperfAnalyticsAdapter_spec.js index b316b44617a..160529125d3 100644 --- a/test/spec/modules/pubperfAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubperfAnalyticsAdapter_spec.js @@ -49,7 +49,8 @@ describe('Pubperf Analytics Adapter', function() { events.emit(constants.EVENTS.AUCTION_END, {}); events.emit(constants.EVENTS.BID_TIMEOUT, {}); - sinon.assert.callCount(pubperfAnalytics.track, 6); + // 6 Pubperf events + 1 Clean.io event + sinon.assert.callCount(pubperfAnalytics.track, 7); }); }); }); diff --git a/test/spec/modules/pubstackAnalyticsAdapter_spec.js b/test/spec/modules/pubstackAnalyticsAdapter_spec.js index 4df25f1665b..3d01a0bb506 100644 --- a/test/spec/modules/pubstackAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubstackAnalyticsAdapter_spec.js @@ -33,6 +33,7 @@ describe('Pubstack Analytics Adapter', () => { events.emit(constants.EVENTS.NO_BID, args) // Then - expect(queue.length).to.eql(6); + // 6 Pubstack events + 1 Clean.io event + expect(queue.length).to.eql(7); }); }); diff --git a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js index 3be4ea3d69c..dd5c223b767 100644 --- a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js @@ -71,10 +71,10 @@ describe('PubWise Prebid Analytics', function () { events.emit(constants.EVENTS.AUCTION_END, {}); // eslint-disable-next-line - //console.log(requests); + //console.log(requests); /* testing for 6 calls, including the 2 we're not currently tracking */ - sandbox.assert.callCount(pubwiseAnalytics.track, 7); + sandbox.assert.callCount(pubwiseAnalytics.track, 8); }); it('should initialize the auction properly', function () { @@ -92,7 +92,7 @@ describe('PubWise Prebid Analytics', function () { let request = requests[0]; let data = JSON.parse(request.requestBody); // eslint-disable-next-line - // console.log(data.metaData); + // console.log(data.metaData); expect(data.metaData, 'metaData property').to.exist; expect(data.metaData.pbjs_version, 'pbjs version').to.equal('$prebid.version$') expect(data.metaData.session_id, 'session id').not.to.be.empty diff --git a/test/spec/modules/sigmoidAnalyticsAdapter_spec.js b/test/spec/modules/sigmoidAnalyticsAdapter_spec.js index 854c3a8e22d..6d772de02eb 100644 --- a/test/spec/modules/sigmoidAnalyticsAdapter_spec.js +++ b/test/spec/modules/sigmoidAnalyticsAdapter_spec.js @@ -38,7 +38,7 @@ describe('sigmoid Prebid Analytic', function () { events.emit(constants.EVENTS.BID_RESPONSE, {}); events.emit(constants.EVENTS.BID_WON, {}); - sinon.assert.callCount(sigmoidAnalytic.track, 7); + sinon.assert.callCount(sigmoidAnalytic.track, 8); }); }); describe('build utm tag data', function () { diff --git a/test/spec/modules/sovrnAnalyticsAdapter_spec.js b/test/spec/modules/sovrnAnalyticsAdapter_spec.js index d6795331417..8284bb54e9b 100644 --- a/test/spec/modules/sovrnAnalyticsAdapter_spec.js +++ b/test/spec/modules/sovrnAnalyticsAdapter_spec.js @@ -202,7 +202,8 @@ describe('Sovrn Analytics Adapter', function () { events.emit(constants.EVENTS.BID_RESPONSE, {}); events.emit(constants.EVENTS.BID_WON, {}); - sinon.assert.callCount(sovrnAnalyticsAdapter.track, 5); + // 5 SovrnAnalytics events + 1 Clean.io event + sinon.assert.callCount(sovrnAnalyticsAdapter.track, 6); }); it('should catch no events if no affiliate id', function () { diff --git a/test/spec/modules/yieldoneAnalyticsAdapter_spec.js b/test/spec/modules/yieldoneAnalyticsAdapter_spec.js index f77bc7bc7ba..f55577913a5 100644 --- a/test/spec/modules/yieldoneAnalyticsAdapter_spec.js +++ b/test/spec/modules/yieldoneAnalyticsAdapter_spec.js @@ -269,7 +269,11 @@ describe('Yieldone Prebid Analytic', function () { setTimeout(function() { events.emit(constants.EVENTS.BID_WON, winner); - sinon.assert.callCount(sendStatStub, 2); + sinon.assert.callCount(sendStatStub, 2) + const billableEventIndex = yieldoneAnalytics.eventsStorage[auctionId].events.findIndex(event => event.eventType === constants.EVENTS.BILLABLE_EVENT); + if (billableEventIndex > -1) { + yieldoneAnalytics.eventsStorage[auctionId].events.splice(billableEventIndex, 1); + } expect(yieldoneAnalytics.eventsStorage[auctionId]).to.deep.equal(wonExpectedResult); delete yieldoneAnalytics.eventsStorage[auctionId]; From 2afac1ed2a531a72fc206358bcad38a51d9788ad Mon Sep 17 00:00:00 2001 From: Abdullah Al Mamun Oronno Date: Mon, 8 Aug 2022 19:10:11 -0400 Subject: [PATCH 111/569] Fix broken download link (#8800) Download link https://prebid.org/download.html no longer valid. Updated with current link https://docs.prebid.org/download.html --- RELEASE_SCHEDULE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_SCHEDULE.md b/RELEASE_SCHEDULE.md index b68495ed4ae..45f4e6c7dc5 100644 --- a/RELEASE_SCHEDULE.md +++ b/RELEASE_SCHEDULE.md @@ -12,7 +12,7 @@ We aim to push a new release of Prebid.js every week on Tuesday. While the releases will be available immediately for those using direct Git access, -it will be about a week before the Prebid Org [Download Page](http://prebid.org/download.html) will be updated. +it will be about a week before the Prebid Org [Download Page](https://docs.prebid.org/download.html) will be updated. You can determine what is in a given build using the [releases page](https://github.com/prebid/Prebid.js/releases) From 9d8112a0e395bd37d3baa3482dad7da7bffec221 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 9 Aug 2022 04:43:08 +0530 Subject: [PATCH 112/569] PubMatic Bid Adapter: Sending allowedAlternateBidderCodes data to AdServer (#8790) * Changed net revenue to True * Added bidderCode and adapterCode to bidObject * setting seat as default adapterCode * removed bidderCode * Added unit test cases * cloned the test object * send alternateBidderCodes to adserver * handled blank and duplicate entries * added test cases * Handled case for empty biddersArray Co-authored-by: Azhar --- modules/pubmaticBidAdapter.js | 21 ++++++-- test/spec/modules/pubmaticBidAdapter_spec.js | 57 ++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 42c05f765bc..bdc8010618c 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1,4 +1,4 @@ -import { logWarn, _each, isBoolean, isStr, isArray, inIframe, mergeDeep, deepAccess, isNumber, deepSetValue, logInfo, logError, deepClone, convertTypes } from '../src/utils.js'; +import { logWarn, _each, isBoolean, isStr, isArray, inIframe, mergeDeep, deepAccess, isNumber, deepSetValue, logInfo, logError, deepClone, convertTypes, uniques } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -177,6 +177,8 @@ let publisherId = 0; let isInvalidNativeRequest = false; let NATIVE_ASSET_ID_TO_KEY_MAP = {}; let NATIVE_ASSET_KEY_TO_ASSET_MAP = {}; +let biddersList = ['pubmatic']; +const allBiddersList = ['all']; // loading NATIVE_ASSET_ID_TO_KEY_MAP _each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] = anAsset.KEY }); @@ -1089,10 +1091,21 @@ export const spec = { payload.ext.wrapper.wv = $$REPO_AND_VERSION$$; payload.ext.wrapper.transactionId = conf.transactionId; payload.ext.wrapper.wp = 'pbjs'; - if (bidderRequest && bidderRequest.bidderCode) { - payload.ext.allowAlternateBidderCodes = bidderSettings.get(bidderRequest.bidderCode, 'allowAlternateBidderCodes'); - payload.ext.allowedAlternateBidderCodes = bidderSettings.get(bidderRequest.bidderCode, 'allowedAlternateBidderCodes'); + const allowAlternateBidder = bidderRequest ? bidderSettings.get(bidderRequest.bidderCode, 'allowAlternateBidderCodes') : undefined; + if (allowAlternateBidder !== undefined) { + payload.ext.marketplace = {}; + if (bidderRequest && allowAlternateBidder == true) { + let allowedBiddersList = bidderSettings.get(bidderRequest.bidderCode, 'allowedAlternateBidderCodes'); + if (isArray(allowedBiddersList)) { + allowedBiddersList = allowedBiddersList.map(val => val.trim().toLowerCase()).filter(val => !!val).filter(uniques) + biddersList = allowedBiddersList.includes('*') ? allBiddersList : [...biddersList, ...allowedBiddersList]; + } else { + biddersList = allBiddersList; + } + } + payload.ext.marketplace.allowedbidders = biddersList.filter(uniques); } + payload.user.gender = (conf.gender ? conf.gender.trim() : UNDEFINED); payload.user.geo = {}; payload.user.geo.lat = _parseSlotParam('lat', conf.lat); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 6e65e725024..f3a49a6a925 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -3,6 +3,7 @@ import {spec, checkVideoPlacement} from 'modules/pubmaticBidAdapter.js'; import * as utils from 'src/utils.js'; import {config} from 'src/config.js'; import { createEidsArray } from 'modules/userId/eids.js'; +import { bidderSettings } from 'src/bidderSettings.js'; const constants = require('src/constants.json'); describe('PubMatic adapter', function () { @@ -1104,6 +1105,62 @@ describe('PubMatic adapter', function () { expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); }); + describe('Marketplace parameters', function() { + let bidderSettingStub; + beforeEach(function() { + bidderSettingStub = sinon.stub(bidderSettings, 'get'); + }); + + afterEach(function() { + bidderSettingStub.restore(); + }); + + it('should not be present when allowAlternateBidderCodes is undefined', function () { + bidderSettingStub.returns(undefined); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + expect(data.ext.marketplace).to.equal(undefined); + }); + + it('should be pubmatic and groupm when allowedAlternateBidderCodes is \'groupm\'', function () { + bidderSettingStub.withArgs('pubmatic', 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs('pubmatic', 'allowedAlternateBidderCodes').returns(['groupm']); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id', + bidderCode: 'pubmatic' + }); + let data = JSON.parse(request.data); + expect(data.ext.marketplace.allowedbidders).to.be.an('array'); + expect(data.ext.marketplace.allowedbidders.length).to.equal(2); + expect(data.ext.marketplace.allowedbidders[0]).to.equal('pubmatic'); + expect(data.ext.marketplace.allowedbidders[1]).to.equal('groupm'); + }); + + it('should be ALL by default', function () { + bidderSettingStub.returns(true); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + expect(data.ext.marketplace.allowedbidders).to.be.an('array'); + expect(data.ext.marketplace.allowedbidders[0]).to.equal('all'); + }); + + it('should be ALL when allowedAlternateBidderCodes is \'*\'', function () { + bidderSettingStub.withArgs('pubmatic', 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs('pubmatic', 'allowedAlternateBidderCodes').returns(['*']); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id', + bidderCode: 'pubmatic' + }); + let data = JSON.parse(request.data); + expect(data.ext.marketplace.allowedbidders).to.be.an('array'); + expect(data.ext.marketplace.allowedbidders[0]).to.equal('all'); + }); + }) + it('Set content from config, set site.content', function() { let sandbox = sinon.sandbox.create(); const content = { From 84499f743115d6c4bf8c33de62eb23ce5a3843ff Mon Sep 17 00:00:00 2001 From: julian-burger-ttd <105891200+julian-burger-ttd@users.noreply.github.com> Date: Mon, 8 Aug 2022 20:12:08 -0700 Subject: [PATCH 113/569] Ttd Bid adapter: support for transaction id, bcat, and consolidate params (#8679) * remove siteId parameter make placementId parameter optional if GPID is passed pass transactionId through to source.tid read bcat from ortb2 * enforce check for gpid or placementId * add error message * Support badv/battr, get tid from ext object * Address review feedback Co-authored-by: Minh Daole --- modules/ttdBidAdapter.js | 63 ++++++---- modules/ttdBidAdapter.md | 14 +-- test/spec/modules/ttdBidAdapter_spec.js | 147 ++++++++++++++++++------ 3 files changed, 156 insertions(+), 68 deletions(-) diff --git a/modules/ttdBidAdapter.js b/modules/ttdBidAdapter.js index 55d7490a4ad..b95b379d74d 100644 --- a/modules/ttdBidAdapter.js +++ b/modules/ttdBidAdapter.js @@ -4,7 +4,7 @@ import { createEidsArray } from './userId/eids.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -const BIDADAPTERVERSION = 'TTD-PREBID-2022.02.18'; +const BIDADAPTERVERSION = 'TTD-PREBID-2022.06.28'; const BIDDER_CODE = 'ttd'; const BIDDER_CODE_LONG = 'thetradedesk'; const BIDDER_ENDPOINT = 'https://direct.adsrvr.org/bid/bidder/'; @@ -58,7 +58,9 @@ function getBidFloor(bid) { } function getSource(validBidRequests) { - let source = {}; + let source = { + tid: validBidRequests[0].transactionId + }; if (validBidRequests[0].schain) { utils.deepSetValue(source, 'ext.schain', validBidRequests[0].schain); } @@ -124,7 +126,6 @@ function getUser(bidderRequest) { function getSite(bidderRequest, firstPartyData) { var site = { - id: utils.deepAccess(bidderRequest, 'bids.0.params.siteId'), page: utils.deepAccess(bidderRequest, 'refererInfo.page'), publisher: { id: utils.deepAccess(bidderRequest, 'bids.0.params.publisherId'), @@ -141,15 +142,20 @@ function getSite(bidderRequest, firstPartyData) { function getImpression(bidRequest) { let impression = { - id: bidRequest.bidId, - tagid: bidRequest.params.placementId + id: bidRequest.bidId }; - let gpid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); - if (gpid) { - impression.ext = { - gpid: gpid - } + const gpid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); + const tid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.tid'); + if (gpid || tid) { + impression.ext = {} + if (gpid) { impression.ext.gpid = gpid } + if (tid) { impression.ext.tid = tid } + } + + const tagid = gpid || bidRequest.params.placementId; + if (tagid) { + impression.tagid = tagid } const mediaTypesVideo = utils.deepAccess(bidRequest, 'mediaTypes.video'); @@ -212,6 +218,12 @@ function banner(bid) { format: sizes, }, optionalParams); + + const battr = utils.deepAccess(bid, 'ortb2Imp.battr'); + if (battr) { + banner.battr = battr + } + return banner; } @@ -279,6 +291,11 @@ function video(bid) { video.maxbitrate = maxbitrate; } + const battr = utils.deepAccess(bid, 'ortb2Imp.battr'); + if (battr) { + video.battr = battr + } + return video; } @@ -318,20 +335,10 @@ export const spec = { utils.logWarn(BIDDER_CODE + ': params.publisherId must be 32 characters or less'); return false; } - if (!bid.params.siteId) { - utils.logWarn(BIDDER_CODE + ': Missing required parameter params.siteId'); - return false; - } - if (bid.params.siteId.length > 50) { - utils.logWarn(BIDDER_CODE + ': params.siteId must be 50 characters or less'); - return false; - } - if (!bid.params.placementId) { - utils.logWarn(BIDDER_CODE + ': Missing required parameter params.placementId'); - return false; - } - if (bid.params.placementId.length > 128) { - utils.logWarn(BIDDER_CODE + ': params.placementId must be 128 characters or less'); + + const gpid = utils.deepAccess(bid, 'ortb2Imp.ext.gpid'); + if (!bid.params.placementId && !gpid) { + utils.logWarn(BIDDER_CODE + ': one of params.placementId or gpid (via the GPT module https://docs.prebid.org/dev-docs/modules/gpt-pre-auction.html) must be passed'); return false; } @@ -387,6 +394,14 @@ export const spec = { ext: getExt(firstPartyData) } + if (firstPartyData && firstPartyData.bcat) { + topLevel.bcat = firstPartyData.bcat; + } + + if (firstPartyData && firstPartyData.badv) { + topLevel.badv = firstPartyData.badv; + } + let url = BIDDER_ENDPOINT + bidderRequest.bids[0].params.supplySourceId; let serverRequest = { diff --git a/modules/ttdBidAdapter.md b/modules/ttdBidAdapter.md index 9c67f3267cb..efdef751149 100644 --- a/modules/ttdBidAdapter.md +++ b/modules/ttdBidAdapter.md @@ -29,9 +29,7 @@ The Trade Desk bid adapter supports Banner and Video. bidder: 'ttd', params: { supplySourceId: 'supplier', - publisherId: '1427ab10f2e448057ed3b422', - siteId: 'site-123', - placementId: 'footer1' + publisherId: '1427ab10f2e448057ed3b422' } } ] @@ -51,8 +49,7 @@ The Trade Desk bid adapter supports Banner and Video. params: { supplySourceId: 'supplier', publisherId: '1427ab10f2e448057ed3b422', - siteId: 'site-123', - placementId: 'footer1', + placementId: '/1111/home#header', banner: { expdir: [1, 3] }, @@ -77,9 +74,7 @@ The Trade Desk bid adapter supports Banner and Video. bidder: 'ttd', params: { supplySourceId: 'supplier', - publisherId: '1427ab10f2e448057ed3b422', - siteId: 'site-123', - placementId: 'footer1' + publisherId: '1427ab10f2e448057ed3b422' } } ] @@ -112,8 +107,7 @@ The Trade Desk bid adapter supports Banner and Video. params: { supplySourceId: 'supplier', publisherId: '1427ab10f2e448057ed3b422', - siteId: 'site-123', - placementId: 'footer1' + placementId: '/1111/home#header' } } ] diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index b933a5dd0c9..aa2e84b619e 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -17,8 +17,7 @@ describe('ttdBidAdapter', function () { 'params': { 'supplySourceId': 'supplier', 'publisherId': '22222222', - 'placementId': 'some-PlacementId_1', - 'siteId': 'testSiteId' + 'placementId': 'some-PlacementId_1' }, 'mediaTypes': { 'banner': { @@ -57,27 +56,20 @@ describe('ttdBidAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when siteId not passed', function () { - let bid = makeBid(); - delete bid.params.siteId; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when siteId is longer than 50 characters', function () { - let bid = makeBid(); - bid.params.siteId = '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111' - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when placementId not passed', function () { + it('should return true if placementId is not passed and gpid is passed', function () { let bid = makeBid(); delete bid.params.placementId; - expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.ortb2Imp = { + ext: { + gpid: '/1111/home#header' + } + } + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when the placementId is longer than 128 characters', function () { + it('should return false if neither placementId nor gpid is passed', function () { let bid = makeBid(); - bid.params.placementId = '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'; // 130 characters + delete bid.params.placementId; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -103,7 +95,6 @@ describe('ttdBidAdapter', function () { 'params': { 'supplySourceId': 'supplier', 'publisherId': '22222222', - 'siteId': 'testSiteId123', 'placementId': 'somePlacementId' }, 'mediaTypes': { @@ -187,17 +178,21 @@ describe('ttdBidAdapter', function () { 'params': { 'supplySourceId': 'supplier', 'publisherId': '13144370', - 'placementId': '1gaa015', - 'siteId': 'testSiteId123' + 'placementId': '1gaa015' }, 'mediaTypes': { 'banner': { 'sizes': [[300, 250], [300, 600]] } }, + 'ortb2Imp': { + 'ext': { + 'tid': '8651474f-58b1-4368-b812-84f8c937a099', + } + }, 'sizes': [[300, 250], [300, 600]], + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '8651474f-58b1-4368-b812-84f8c937a099', 'bidId': '243310435309b5', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', @@ -240,16 +235,49 @@ describe('ttdBidAdapter', function () { expect(url).to.equal('https://direct.adsrvr.org/bid/bidder/supplier'); }); - it('sends publisher id, site id, and placement id', function () { + it('sends publisher id', function () { const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; expect(requestBody.site).to.be.not.null; expect(requestBody.site.publisher).to.be.not.null; - expect(requestBody.imp[0].tagid).to.be.not.null; expect(requestBody.site.publisher.id).to.equal(baseBannerBidRequests[0].params.publisherId); - expect(requestBody.site.id).to.equal(baseBannerBidRequests[0].params.siteId); + }); + + it('sends placement id in tagid', function () { + const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; expect(requestBody.imp[0].tagid).to.equal(baseBannerBidRequests[0].params.placementId); }); + it('sends gpid in tagid if present', function () { + let clonedBannerRequests = deepClone(baseBannerBidRequests); + const gpid = '/1111/home#header'; + clonedBannerRequests[0].ortb2Imp = { + ext: { + gpid: gpid + } + }; + const requestBody = testBuildRequests(clonedBannerRequests, baseBidderRequest).data; + expect(requestBody.imp[0].tagid).to.equal(gpid); + }); + + it('sends gpid in ext.gpid if present', function () { + let clonedBannerRequests = deepClone(baseBannerBidRequests); + const gpid = '/1111/home#header'; + clonedBannerRequests[0].ortb2Imp = { + ext: { + gpid: gpid + } + }; + const requestBody = testBuildRequests(clonedBannerRequests, baseBidderRequest).data; + expect(requestBody.imp[0].ext).to.be.not.null; + expect(requestBody.imp[0].ext.gpid).to.equal(gpid); + }); + + it('sends transaction id in source.tid', function () { + const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; + expect(requestBody.source).to.be.not.null; + expect(requestBody.source.tid).to.equal('1111474f-58b1-4368-b812-84f8c937a099'); + }); + it('includes the ad size in the bid request', function () { const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; expect(requestBody.imp[0].banner.format[0].w).to.equal(300); @@ -283,18 +311,44 @@ describe('ttdBidAdapter', function () { }); it('sets keywords properly if sent', function () { - let clonedBannerRequests = deepClone(baseBannerBidRequests); - const ortb2 = { site: { keywords: 'highViewability, clothing, holiday shopping' } }; - const requestBody = testBuildRequests(clonedBannerRequests, {...baseBidderRequest, ortb2}).data; + const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; config.resetConfig(); expect(requestBody.ext.ttdprebid.keywords).to.deep.equal(['highViewability', 'clothing', 'holiday shopping']); }); + it('sets bcat properly if sent', function () { + const ortb2 = { + bcat: ['IAB1-1', 'IAB2-9'] + }; + const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + config.resetConfig(); + expect(requestBody.bcat).to.deep.equal(['IAB1-1', 'IAB2-9']); + }); + + it('sets badv properly if sent', function () { + const ortb2 = { + badv: ['adv1.com', 'adv2.com'] + }; + const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + config.resetConfig(); + expect(requestBody.badv).to.deep.equal(['adv1.com', 'adv2.com']); + }); + + it('sets battr properly if present', function () { + let clonedBannerRequests = deepClone(baseBannerBidRequests); + const battr = [1, 2, 3]; + clonedBannerRequests[0].ortb2Imp = { + battr: battr + }; + const requestBody = testBuildRequests(clonedBannerRequests, baseBidderRequest).data; + expect(requestBody.imp[0].banner.battr).to.equal(battr); + }); + it('sets ext properly', function () { let clonedBannerRequests = deepClone(baseBannerBidRequests); @@ -442,9 +496,14 @@ describe('ttdBidAdapter', function () { 'sizes': [[300, 250], [300, 600]] } }, + 'ortb2Imp': { + 'ext': { + 'tid': '8651474f-58b1-4368-b812-84f8c937a099', + } + }, 'sizes': [[300, 250], [300, 600]], + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '8651474f-58b1-4368-b812-84f8c937a099', 'bidId': 'small', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', @@ -454,17 +513,21 @@ describe('ttdBidAdapter', function () { 'bidder': 'ttd', 'params': { 'publisherId': '13144370', - 'placementId': 'top', - 'siteId': 'testSite123' + 'placementId': 'top' }, 'mediaTypes': { 'banner': { 'sizes': [[728, 90]] } }, + 'ortb2Imp': { + 'ext': { + 'tid': '12345678-58b1-4368-b812-84f8c937a099', + } + }, 'sizes': [[728, 90]], - 'adUnitCode': 'div-gpt-ad-91515710-0', 'transactionId': '825c1228-ca8c-4657-b40f-2df500621527', + 'adUnitCode': 'div-gpt-ad-91515710-0', 'bidId': 'large', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', @@ -493,6 +556,12 @@ describe('ttdBidAdapter', function () { it('sends multiple impressions', function () { const requestBody = testBuildRequests(baseBannerMultipleBidRequests, baseBidderRequest).data; expect(requestBody.imp.length).to.equal(2); + expect(requestBody.source).to.be.not.null; + expect(requestBody.source.tid).to.equal('1111474f-58b1-4368-b812-84f8c937a099'); + expect(requestBody.imp[0].ext).to.be.not.null; + expect(requestBody.imp[0].ext.tid).to.equal('8651474f-58b1-4368-b812-84f8c937a099'); + expect(requestBody.imp[1].ext).to.be.not.null; + expect(requestBody.imp[1].ext.tid).to.equal('12345678-58b1-4368-b812-84f8c937a099'); }); it('sends the right tag ids for each ad unit', function () { @@ -547,8 +616,13 @@ describe('ttdBidAdapter', function () { 'sizes': [[300, 250], [300, 600]] } }, + 'ortb2Imp': { + 'ext': { + 'tid': '8651474f-58b1-4368-b812-84f8c937a099', + } + }, + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '8651474f-58b1-4368-b812-84f8c937a099', 'bidId': '243310435309b5', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', @@ -607,8 +681,13 @@ describe('ttdBidAdapter', function () { 'maxduration': 30 } }, + 'ortb2Imp': { + 'ext': { + 'tid': '8651474f-58b1-4368-b812-84f8c937a099', + } + }, + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '8651474f-58b1-4368-b812-84f8c937a099', 'bidId': '243310435309b5', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', From 555c4c0b9c01c05b8cb4a91fd0822a7e3f9ef6ce Mon Sep 17 00:00:00 2001 From: JacobKlein26 <42449375+JacobKlein26@users.noreply.github.com> Date: Mon, 8 Aug 2022 23:13:45 -0400 Subject: [PATCH 114/569] NextMillennium Bid Adapter: Add referrer and imp to bid request (#8718) * add more information and testing * fix typo * fix typo in testing * use getRefererInfo Co-authored-by: Mikhail Ivanchenko --- modules/nextMillenniumBidAdapter.js | 23 +++++++++++++++++-- .../modules/nextMillenniumBidAdapter_spec.js | 17 ++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 375258176da..f873e5b5c29 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -1,4 +1,5 @@ import { isStr, _each, parseUrl, getWindowTop, getBidIdParameter } from '../src/utils.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; @@ -24,12 +25,18 @@ export const spec = { _each(validBidRequests, function(bid) { window.nmmRefreshCounts[bid.adUnitCode] = window.nmmRefreshCounts[bid.adUnitCode] || 0; + const id = getPlacementId(bid) + + if (bid.sizes && !Array.isArray(bid.sizes[0])) bid.sizes = [bid.sizes] + if (!bid.ortb2) bid.ortb2 = {} + if (!bid.ortb2.device) bid.ortb2.device = {} + bid.ortb2.device.referrer = (getRefererInfo && getRefererInfo().ref) || '' const postBody = { 'id': bid.auctionId, 'ext': { 'prebid': { 'storedrequest': { - 'id': getPlacementId(bid) + 'id': id } }, @@ -39,7 +46,19 @@ export const spec = { 'scrollTop': window.pageYOffset || document.documentElement.scrollTop } }, - ...bid.ortb2 + ...bid.ortb2, + 'imp': [{ + 'banner': { + 'format': (bid.sizes || []).map(s => { return {w: s[0], h: s[1]} }) + }, + 'ext': { + 'prebid': { + 'storedrequest': { + 'id': id + } + } + } + }] } const gdprConsent = bidderRequest && bidderRequest.gdprConsent; diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 9362388b539..40005356fd8 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -114,6 +114,23 @@ describe('nextMillenniumBidAdapterTests', function() { expect(JSON.parse(request[0].data).ext.nextMillennium.elOffsets).to.be.an('object') }) + it('Check if refferer was added', function() { + const request = spec.buildRequests(bidRequestData) + expect(JSON.parse(request[0].data).device.referrer).to.exist + }) + + it('Check if imp object was added', function() { + const request = spec.buildRequests(bidRequestData) + expect(JSON.parse(request[0].data).imp).to.be.an('array') + }) + + it('Check if imp prebid stored id is correct', function() { + const request = spec.buildRequests(bidRequestData) + const requestData = JSON.parse(request[0].data); + const storedReqId = requestData.ext.prebid.storedrequest.id; + expect(requestData.imp[0].ext.prebid.storedrequest.id).to.equal(storedReqId) + }) + it('Test getUserSyncs function', function () { const syncOptions = { 'iframeEnabled': true From 638e7c5866fa62093f9fea074554d90c3cb5cf23 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 8 Aug 2022 20:14:19 -0700 Subject: [PATCH 115/569] UserID: continue the auction if userId init fails (#8788) --- modules/userId/index.js | 16 ++++++++++------ test/spec/modules/userId_spec.js | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index f42c4c7827f..f925e637178 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -614,9 +614,9 @@ function getPPID() { * @param {Object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids. * @param {function} fn required; The next function in the chain, used by hook.js */ -export function requestBidsHook(fn, reqBidsConfigObj, {delay = GreedyPromise.timeout} = {}) { +export function requestBidsHook(fn, reqBidsConfigObj, {delay = GreedyPromise.timeout, getIds = getUserIdsAsync} = {}) { GreedyPromise.race([ - getUserIdsAsync(), + getIds().catch(() => null), delay(auctionDelay) ]).then(() => { // pass available user id data to bid adapters @@ -762,13 +762,17 @@ function refreshUserIds({submoduleNames} = {}, callback) { function getUserIdsAsync() { return initIdSystem().then( () => getUserIds(), - (e) => - e === INIT_CANCELED + (e) => { + if (e === INIT_CANCELED) { // there's a pending refresh - because GreedyPromise runs this synchronously, we are now in the middle // of canceling the previous init, before the refresh logic has had a chance to run. // Use a "normal" Promise to clear the stack and let it complete (or this will just recurse infinitely) - ? Promise.resolve().then(getUserIdsAsync) - : GreedyPromise.reject(e) + return Promise.resolve().then(getUserIdsAsync) + } else { + logError('Error initializing userId', e) + return GreedyPromise.reject(e) + } + } ); } diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 046b3ffd321..e6058673d41 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -539,9 +539,22 @@ describe('User ID', function () { clearStack().then(() => { // simulate init complete mockIdCallback.callArg(0, {id: {MOCKID: '1111'}}); - }) + }); }); + it('should continue the auction when init fails', (done) => { + startInit(); + requestBidsHook(() => { + done(); + }, + {adUnits: [getAdUnitMock()]}, + { + delay: delay(), + getIds: () => Promise.reject(new Error()) + } + ); + }) + it('should not get stuck when init fails', () => { const err = new Error(); mockIdCallback.callsFake(() => { throw err; }); From a5b4cab0b185927d73b780bffdd27674aafc6432 Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Tue, 9 Aug 2022 05:28:10 +0200 Subject: [PATCH 116/569] sspBC bid adapter - remove storage/cookie check, add screen size to request (#8762) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update tests for sspBC adapter Update tests for sspBC adapter: - change userSync test (due to tcf param appended in v4.6) - add tests for onBidWon and onTimeout * [sspbc-adapter] 5.3 updates: content-type for notifications * [sspbc-adapter] pass CTA to native bid * [sspbc-5.3] keep pbsize for detected adunits * [sspbc-5.3] increment adaptor ver * [sspbc-adapter] maintenance update to sspBCBidAdapter * remove yarn.lock * Delete package-lock.json * remove package-lock.jsonfrom pull request * [sspbc-adapter] send pageViewId in request * [sspbc-adapter] update pageViewId test * [sspbc-adapter] add viewabiility tracker to native ads * [sspbc-adapter] add support for bid.admNative property * [sspbc-adapter] ensure that placement id length is always 3 (improves matching response to request) * [sspbc-adapter] read publisher id and custom ad label, then send them to banner creative * [sspbc-adapter] adlabel and pubid are set as empty strings, if not present in bid response * [sspbc-adapter] jstracker data fix * [sspbc-adapter] jstracker data fix * [sspbc-adapter] send tagid in notifications * [sspbc-adapter] add gvlid to spec; prepare getUserSyncs for iframe + image sync * [sspbc-adapter] fix notification payload * [sspbc-adapter] fix notification payload, fix tests * [sspbc-adapter] add userIds to ortb request * [sspbc-adapter] update to 4.1, change request to be ortb 2.6 compliant * [sspbc-adapter] update tests * [ssbc-adapter] bid cache for video ads * [sspbc-adapter] add PageView.id to banner ad; update tests * [sspbc-adapter] fix window.gam not being added to banner html * [sspbc-adapter] send device / content language * [sspbc-adapter] send pageview and site ids to user sync frame * [sspbc-adapter] add ES6 version of common ad library (for banner creatives) * [sspbc-adapter] move content property * [sspbc-adapter] reorganize notification payload creator * [sspbc-adapter] store PLN price in meta; send in bidWon notification * [sspbc-adapter] add playbackmethod to supporten video params; allow overridinbg video settngs via bid.params.video * [sspbc-adapter] update md * [sspbc-adapter] fix error in mapVideo method (Object assign merror when mediaTypes do not contain video) * [sspbc-5.7] remove storage/cookie detection * [sspbc-5.7] add screen size to request Co-authored-by: Wojciech Biały --- modules/sspBCBidAdapter.js | 29 ++++++++++------------- test/spec/modules/sspBCBidAdapter_spec.js | 4 ++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 94dbe9a4468..0a19410c78f 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -1,10 +1,9 @@ -import {deepAccess, getWindowTop, isArray, logWarn} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {includes as strIncludes} from '../src/polyfill.js'; -import { getStorageManager } from '../src/storageManager.js'; +import { deepAccess, getWindowTop, isArray, logWarn } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { includes as strIncludes } from '../src/polyfill.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'sspBC'; @@ -13,9 +12,8 @@ const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TRACKER_URL = 'https://bdr.wpcdn.pl/tag/jstracker.js'; const GVLID = 676; -const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); const TMAX = 450; -const BIDDER_VERSION = '5.6'; +const BIDDER_VERSION = '5.7'; const DEFAULT_CURRENCY = 'PLN'; const W = window; const { navigator } = W; @@ -102,11 +100,6 @@ const getNotificationPayload = bidData => { } } -const cookieSupport = () => { - const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent); - return !isSafari && storage.cookiesAreEnabled(); -}; - const applyClientHints = ortbRequest => { const { location } = document; const { connection = {}, deviceMemory, userAgentData = {} } = navigator; @@ -569,7 +562,11 @@ const spec = { tmax, user: {}, regs: {}, - device: { language: getBrowserLanguage() }, + device: { + language: getBrowserLanguage(), + w: screen.width, + h: screen.height, + }, test: testMode, }; @@ -579,7 +576,7 @@ const spec = { return { method: 'POST', - url: `${BIDDER_URL}?cs=${cookieSupport()}&bdver=${BIDDER_VERSION}&pbver=${pbver}&inver=0`, + url: `${BIDDER_URL}?bdver=${BIDDER_VERSION}&pbver=${pbver}&inver=0`, data: JSON.stringify(payload), bidderRequest, }; diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index f286eb3a1ff..d182b9db24c 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -678,8 +678,8 @@ describe('SSPBC adapter', function () { }); it('should send no syncs, if frame sync is not allowed', function () { - expect(syncResultImage).to.have.length(0); ; - expect(syncResultNone).to.have.length(0); ; + expect(syncResultImage).to.have.length(0); + expect(syncResultNone).to.have.length(0); }); }); From ec2117323b23c3084149292b8610f7bed3749534 Mon Sep 17 00:00:00 2001 From: Rupesh Lakhani <35333377+AskRupert-DM@users.noreply.github.com> Date: Tue, 9 Aug 2022 14:18:56 +0100 Subject: [PATCH 117/569] Ozone Bid Adapter: Various improvements (#8755) * ozone adapter 2.8.0 added instream support fixed/cleaned up code * spec test for ozone 2.8.0 adapter * ozone adapter 2.8.0 * Update ozoneBidAdapter_spec.js * Update ozoneBidAdapter.md Co-authored-by: Patrick McCann --- modules/ozoneBidAdapter.js | 369 +++------ modules/ozoneBidAdapter.md | 37 + test/spec/modules/ozoneBidAdapter_spec.js | 878 +++++++++------------- 3 files changed, 504 insertions(+), 780 deletions(-) diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index bbcb559b53b..2f229720208 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -1,23 +1,30 @@ -import { logInfo, logError, deepAccess, logWarn, deepSetValue, isArray, contains, isStr, mergeDeep } from '../src/utils.js'; +import { + logInfo, + logError, + deepAccess, + logWarn, + deepSetValue, + isArray, + contains, + mergeDeep, + parseUrl +} from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import {config} from '../src/config.js'; import {getPriceBucketString} from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - +import {getRefererInfo} from '../src/refererDetection.js'; const BIDDER_CODE = 'ozone'; - const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & cookie const AUCTIONURI = '/openrtb2/auction'; const OZONECOOKIESYNC = '/static/load-cookie.html'; const OZONE_RENDERER_URL = 'https://prebid.the-ozone-project.com/ozone-renderer.js'; const ORIGIN_DEV = 'https://test.ozpr.net'; - -const OZONEVERSION = '2.7.0'; +const OZONEVERSION = '2.8.0'; export const spec = { gvlid: 524, - aliases: [{code: 'lmc', gvlid: 524}, {code: 'newspassid', gvlid: 524}], + aliases: [{code: 'lmc', gvlid: 524}], version: OZONEVERSION, code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], @@ -31,10 +38,6 @@ export const spec = { 'cookieSyncUrl': ORIGIN + OZONECOOKIESYNC, 'rendererUrl': OZONE_RENDERER_URL }, - /** - * make sure that the whitelabel/default values are available in the propertyBag - * @param bid Object : the bid - */ loadWhitelabelData(bid) { if (this.propertyBag.whitelabel) { return; } this.propertyBag.whitelabel = JSON.parse(JSON.stringify(this.whitelabel_defaults)); @@ -53,7 +56,6 @@ export const spec = { this.propertyBag.whitelabel.auctionUrl = bidderConfig.endpointOverride.origin + AUCTIONURI; this.propertyBag.whitelabel.cookieSyncUrl = bidderConfig.endpointOverride.origin + OZONECOOKIESYNC; } - if (arr.hasOwnProperty('renderer')) { if (arr.renderer.match('%3A%2F%2F')) { this.propertyBag.whitelabel.rendererUrl = decodeURIComponent(arr['renderer']); @@ -92,11 +94,6 @@ export const spec = { getRendererUrl() { return this.propertyBag.whitelabel.rendererUrl; }, - /** - * Basic check to see whether required parameters are in the request. - * @param bid - * @returns {boolean} - */ isBidRequestValid(bid) { this.loadWhitelabelData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); @@ -115,7 +112,7 @@ export const spec = { return false; } if (!(bid.params.publisherId).toString().match(/^[a-zA-Z0-9\-]{12}$/)) { - logError('VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens', adUnitCode); + logError('VALIDATION FAILED : publisherId must be exactly 12 alphanumeric characters including hyphens', adUnitCode); return false; } if (!(bid.params.hasOwnProperty('siteId'))) { @@ -160,19 +157,10 @@ export const spec = { } return true; }, - - /** - * Split this out so that we can validate the placementId and also the override GET parameter ozstoredrequest - * @param placementId - */ isValidPlacementId(placementId) { return placementId.toString().match(/^[0-9]{10}$/); }, - buildRequests(validBidRequests, bidderRequest) { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - this.loadWhitelabelData(validBidRequests[0]); this.propertyBag.buildRequestsStart = new Date().getTime(); let whitelabelBidder = this.propertyBag.whitelabel.bidder; // by default = ozone @@ -193,14 +181,13 @@ export const spec = { singleRequest = singleRequest !== false; // undefined & true will be true logInfo(`config ${whitelabelBidder}.singleRequest : `, singleRequest); let ozoneRequest = {}; // we only want to set specific properties on this, not validBidRequests[0].params - delete ozoneRequest.test; // don't allow test to be set in the config - ONLY use $_GET['pbjs_debug'] - - let fpd = bidderRequest.ortb2; + logInfo('going to get ortb2 from bidder request...'); + let fpd = deepAccess(bidderRequest, 'ortb2', null); + logInfo('got fpd: ', fpd); if (fpd && deepAccess(fpd, 'user')) { logInfo('added FPD user object'); ozoneRequest.user = fpd.user; } - const getParams = this.getGetParametersAsObject(); const wlOztestmodeKey = whitelabelPrefix + 'testmode'; const isTestMode = getParams[wlOztestmodeKey] || null; // this can be any string, it's used for testing ads @@ -212,7 +199,8 @@ export const spec = { let placementId = placementIdOverrideFromGetParam || this.getPlacementId(ozoneBidRequest); // prefer to use a valid override param, else the bidRequest placement Id obj.id = ozoneBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder ozone made bid for unknown request ID: mb7953.859498327448. Ignoring." obj.tagid = placementId; - obj.secure = window.location.protocol === 'https:' ? 1 : 0; + let parsed = parseUrl(getRefererInfo().page); + obj.secure = parsed.protocol === 'https' ? 1 : 0; let arrBannerSizes = []; if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) { if (ozoneBidRequest.hasOwnProperty('sizes')) { @@ -292,7 +280,7 @@ export const spec = { } } if (fpd && deepAccess(fpd, 'site')) { - logInfo('added fpd.site'); + logInfo('adding fpd.site'); if (deepAccess(obj, 'ext.' + whitelabelBidder + '.customData.0.targeting', false)) { obj.ext[whitelabelBidder].customData[0].targeting = Object.assign(obj.ext[whitelabelBidder].customData[0].targeting, fpd.site); } else { @@ -304,7 +292,6 @@ export const spec = { } return obj; }); - let extObj = {}; extObj[whitelabelBidder] = {}; extObj[whitelabelBidder][whitelabelPrefix + '_pb_v'] = OZONEVERSION; @@ -315,8 +302,7 @@ export const spec = { extObj[whitelabelBidder].pubcid = userIds.pubcid; } } - - extObj[whitelabelBidder].pv = this.getPageId(); // attach the page ID that will be common to all auciton calls for this page if refresh() is called + extObj[whitelabelBidder].pv = this.getPageId(); // attach the page ID that will be common to all auction calls for this page if refresh() is called let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') logInfo(`${whitelabelPrefix}_omp_floor dollar value = `, ozOmpFloorDollars); if (typeof ozOmpFloorDollars === 'number') { @@ -327,25 +313,22 @@ export const spec = { let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; extObj[whitelabelBidder][whitelabelPrefix + '_kvp_rw'] = useOzWhitelistAdserverKeys ? 1 : 0; - if (whitelabelBidder != 'ozone') { + if (whitelabelBidder !== 'ozone') { logInfo('setting aliases object'); extObj.prebid = {aliases: {'ozone': whitelabelBidder}}; } - if (getParams.hasOwnProperty('ozf')) { extObj[whitelabelBidder]['ozf'] = getParams.ozf == 'true' || getParams.ozf == 1 ? 1 : 0; } - if (getParams.hasOwnProperty('ozpf')) { extObj[whitelabelBidder]['ozpf'] = getParams.ozpf == 'true' || getParams.ozpf == 1 ? 1 : 0; } + if (getParams.hasOwnProperty('ozf')) { extObj[whitelabelBidder]['ozf'] = getParams.ozf === 'true' || getParams.ozf === '1' ? 1 : 0; } + if (getParams.hasOwnProperty('ozpf')) { extObj[whitelabelBidder]['ozpf'] = getParams.ozpf === 'true' || getParams.ozpf === '1' ? 1 : 0; } if (getParams.hasOwnProperty('ozrp') && getParams.ozrp.match(/^[0-3]$/)) { extObj[whitelabelBidder]['ozrp'] = parseInt(getParams.ozrp); } if (getParams.hasOwnProperty('ozip') && getParams.ozip.match(/^\d+$/)) { extObj[whitelabelBidder]['ozip'] = parseInt(getParams.ozip); } if (this.propertyBag.endpointOverride != null) { extObj[whitelabelBidder]['origin'] = this.propertyBag.endpointOverride; } - - var userExtEids = this.generateEids(validBidRequests); // generate the UserIDs in the correct format for UserId module - + let userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); // generate the UserIDs in the correct format for UserId module ozoneRequest.site = { 'publisher': {'id': htmlParams.publisherId}, - 'page': document.location.href, + 'page': getRefererInfo().page, 'id': htmlParams.siteId }; - ozoneRequest.test = (getParams.hasOwnProperty('pbjs_debug') && getParams['pbjs_debug'] === 'true') ? 1 : 0; - + ozoneRequest.test = config.getConfig('debug') ? 1 : 0; if (bidderRequest && bidderRequest.gdprConsent) { logInfo('ADDING GDPR info'); let apiVersion = deepAccess(bidderRequest, 'gdprConsent.apiVersion', 1); @@ -359,20 +342,18 @@ export const spec = { logInfo('WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object'); } if (bidderRequest && bidderRequest.uspConsent) { - logInfo('ADDING CCPA info'); - deepSetValue(ozoneRequest, 'user.ext.uspConsent', bidderRequest.uspConsent); + logInfo('ADDING USP consent info'); + deepSetValue(ozoneRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } else { - logInfo('WILL NOT ADD CCPA info; no bidderRequest.uspConsent.'); + logInfo('WILL NOT ADD USP consent info; no bidderRequest.uspConsent.'); } if (schain) { // we set this while iterating over the bids logInfo('schain found'); deepSetValue(ozoneRequest, 'source.ext.schain', schain); } - if (config.getConfig('coppa') === true) { deepSetValue(ozoneRequest, 'regs.coppa', 1); } - if (singleRequest) { logInfo('buildRequests starting to generate response for a single request'); ozoneRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. @@ -414,19 +395,6 @@ export const spec = { logInfo(`buildRequests going to return for non-single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, arrRet); return arrRet; }, - /** - * parse a bidRequestRef that contains getFloor(), get all the data from it for the sizes & media requested for this bid & return an object containing floor data you can send to auciton endpoint - * @param bidRequestRef object = a valid bid request object reference - * @return object - * - * call: - * bidObj.getFloor({ - currency: 'USD', <- currency to return the value in - mediaType: ‘banner’, - size: ‘*’ <- or [300,250] or [[300,250],[640,480]] - * }); - * - */ getFloorObjectForAuction(bidRequestRef) { const mediaTypesSizes = { banner: deepAccess(bidRequestRef, 'mediaTypes.banner.sizes', null), @@ -447,16 +415,6 @@ export const spec = { logInfo('getFloorObjectForAuction returning : ', JSON.parse(JSON.stringify(ret))); return ret; }, - /** - * Interpret the response if the array contains BIDDER elements, in the format: [ [bidder1 bid 1, bidder1 bid 2], [bidder2 bid 1, bidder2 bid 2] ] - * NOte that in singleRequest mode this will be called once, else it will be called for each adSlot's response - * - * Updated April 2019 to return all bids, not just the one we decide is the 'winner' - * - * @param serverResponse - * @param request - * @returns {*} - */ interpretResponse(serverResponse, request) { if (request && request.bidderRequest && request.bidderRequest.bids) { this.loadWhitelabelData(request.bidderRequest.bids[0]); } let startTime = new Date().getTime(); @@ -478,15 +436,12 @@ export const spec = { enhancedAdserverTargeting = true; } logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); - serverResponse.seatbid = injectAdIdsIntoAllBidResponses(serverResponse.seatbid); // we now make sure that each bid in the bidresponse has a unique (within page) adId attribute. - serverResponse.seatbid = this.removeSingleBidderMultipleBids(serverResponse.seatbid); let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') let addOzOmpFloorDollars = typeof ozOmpFloorDollars === 'number'; let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; - for (let i = 0; i < serverResponse.seatbid.length; i++) { let sb = serverResponse.seatbid[i]; for (let j = 0; j < sb.bid.length; j++) { @@ -499,17 +454,30 @@ export const spec = { let isVideo = false; let bidType = deepAccess(thisBid, 'ext.prebid.type'); logInfo(`this bid type is : ${bidType}`, j); + let adserverTargeting = {}; if (bidType === VIDEO) { isVideo = true; + this.setBidMediaTypeIfNotExist(thisBid, VIDEO); videoContext = this.getVideoContextForBidId(thisBid.bidId, request.bidderRequest.bids); // should be instream or outstream (or null if error) if (videoContext === 'outstream') { - logInfo('going to attach a renderer to OUTSTREAM video : ', j); + logInfo('going to set thisBid.mediaType = VIDEO & attach a renderer to OUTSTREAM video : ', j); thisBid.renderer = newRenderer(thisBid.bidId); } else { - logInfo('bid is not an outstream video, will not attach a renderer: ', j); + logInfo('bid is not an outstream video, will set thisBid.mediaType = VIDEO and thisBid.vastUrl and not attach a renderer: ', j); + thisBid.vastUrl = `https://${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_host', 'missing_host')}${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_path', 'missing_path')}?id=${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_id', 'missing_id')}`; // need to see if this works ok for ozone + adserverTargeting['hb_cache_host'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_host', 'no-host'); + adserverTargeting['hb_cache_path'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_path', 'no-path'); + if (!thisBid.hasOwnProperty('videoCacheKey')) { + let videoCacheUuid = deepAccess(thisBid, 'ext.prebid.targeting.hb_uuid', 'no_hb_uuid'); + logInfo(`Adding videoCacheKey: ${videoCacheUuid}`); + thisBid.videoCacheKey = videoCacheUuid; + } else { + logInfo('videoCacheKey already exists on the bid object, will not add it'); + } } + } else { + this.setBidMediaTypeIfNotExist(thisBid, BANNER); } - let adserverTargeting = {}; if (enhancedAdserverTargeting) { let allBidsForThisBidid = ozoneGetAllBidsForBidId(thisBid.bidId, serverResponse.seatbid); logInfo('Going to iterate allBidsForThisBidId', allBidsForThisBidid); @@ -552,7 +520,8 @@ export const spec = { adserverTargeting[whitelabelPrefix + '_auc_id'] = String(request.bidderRequest.auctionId); adserverTargeting[whitelabelPrefix + '_winner'] = String(winningSeat); adserverTargeting[whitelabelPrefix + '_bid'] = 'true'; - + adserverTargeting[whitelabelPrefix + '_cache_id'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_id', 'no-id'); + adserverTargeting[whitelabelPrefix + '_uuid'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_uuid', 'no-id'); if (enhancedAdserverTargeting) { adserverTargeting[whitelabelPrefix + '_imp_id'] = String(winningBid.impid); adserverTargeting[whitelabelPrefix + '_pb_v'] = OZONEVERSION; @@ -570,26 +539,24 @@ export const spec = { } } let endTime = new Date().getTime(); - logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`, arrAllBids); + logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`); + logInfo('interpretResponse arrAllBids (serialised): ', JSON.parse(JSON.stringify(arrAllBids))); // this is ok to log because the renderer has not been attached yet return arrAllBids; }, - /** - * Use this to get all config values - * Now it's getting complicated with whitelabeling, this simplifies the code for getting config values. - * eg. to get whitelabelled version you just sent the ozone default string like ozone.oz_omp_floor - * @param ozoneVersion string like 'ozone.oz_omp_floor' - * @return {string|object} - */ + setBidMediaTypeIfNotExist(thisBid, mediaType) { + if (!thisBid.hasOwnProperty('mediaType')) { + logInfo(`setting thisBid.mediaType = ${mediaType}`); + thisBid.mediaType = mediaType; + } else { + logInfo(`found value for thisBid.mediaType: ${thisBid.mediaType}`); + } + }, getWhitelabelConfigItem(ozoneVersion) { - if (this.propertyBag.whitelabel.bidder == 'ozone') { return config.getConfig(ozoneVersion); } + if (this.propertyBag.whitelabel.bidder === 'ozone') { return config.getConfig(ozoneVersion); } let whitelabelledSearch = ozoneVersion.replace('ozone', this.propertyBag.whitelabel.bidder); whitelabelledSearch = whitelabelledSearch.replace('oz_', this.propertyBag.whitelabel.keyPrefix + '_'); return config.getConfig(whitelabelledSearch); }, - /** - * If a bidder bids for > 1 size for an adslot, allow only the highest bid - * @param seatbid object (serverResponse.seatbid) - */ removeSingleBidderMultipleBids(seatbid) { var ret = []; for (let i = 0; i < seatbid.length; i++) { @@ -620,7 +587,7 @@ export const spec = { } if (optionsType.iframeEnabled) { var arrQueryString = []; - if (document.location.search.match(/pbjs_debug=true/)) { + if (config.getConfig('debug')) { arrQueryString.push('pbjs_debug=true'); } arrQueryString.push('gdpr=' + (deepAccess(gdprConsent, 'gdprApplies', false) ? '1' : '0')); @@ -633,7 +600,6 @@ export const spec = { arrQueryString.push('siteId=' + this.cookieSyncBag.siteId); arrQueryString.push('cb=' + Date.now()); arrQueryString.push('bidder=' + this.propertyBag.whitelabel.bidder); - var strQueryString = arrQueryString.join('&'); if (strQueryString.length > 0) { strQueryString = '?' + strQueryString; @@ -645,11 +611,6 @@ export const spec = { }]; } }, - /** - * Find the bid matching the bidId in the request object - * get instream or outstream if this was a video request else null - * @return object|null - */ getBidRequestForBidId(bidId, arrBids) { for (let i = 0; i < arrBids.length; i++) { if (arrBids[i].bidId === bidId) { // bidId in the request comes back as impid in the seatbid bids @@ -658,13 +619,6 @@ export const spec = { } return null; }, - /** - * Locate the bid inside the arrBids for this bidId, then discover the video context, and return it. - * IF the bid cannot be found return null, else return a string. - * @param bidId - * @param arrBids - * @return string|null - */ getVideoContextForBidId(bidId, arrBids) { let requestBid = this.getBidRequestForBidId(bidId, arrBids); if (requestBid != null) { @@ -672,11 +626,6 @@ export const spec = { } return null; }, - /** - * This is used for cookie sync, not auction call - * Look for pubcid & all the other IDs according to http://prebid.org/dev-docs/modules/userId.html - * @return map - */ findAllUserIds(bidRequest) { var ret = {}; let searchKeysSingle = ['pubcid', 'tdid', 'idl_env', 'criteoId', 'lotamePanoramaId', 'fabrickId']; @@ -710,10 +659,6 @@ export const spec = { if (sharedid) { ret['sharedid'] = sharedid; } - let sharedidthird = deepAccess(bidRequest.userId, 'sharedid.third'); - if (sharedidthird) { - ret['sharedidthird'] = sharedidthird; - } } if (!ret.hasOwnProperty('pubcid')) { let pubcid = deepAccess(bidRequest, 'crumbs.pubcid'); @@ -723,20 +668,9 @@ export const spec = { } return ret; }, - /** - * Convenient method to get the value we need for the placementId - ONLY from the bidRequest - NOT taking into account any GET override ID - * @param bidRequest - * @return string - */ getPlacementId(bidRequest) { return (bidRequest.params.placementId).toString(); }, - /** - * GET parameter introduced in 2.2.0 : ozstoredrequest - * IF the GET parameter exists then it must validate for placementId correctly - * IF there's a $_GET['ozstoredrequest'] & it's valid then return this. Else return null. - * @returns null|string - */ getPlacementIdOverrideFromGetParam() { let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; let arr = this.getGetParametersAsObject(); @@ -750,68 +684,19 @@ export const spec = { } return null; }, - /** - * Generate an object we can append to the auction request, containing user data formatted correctly for different ssps - * http://prebid.org/dev-docs/modules/userId.html - * @param validBidRequests - * @return {Array} - */ - generateEids(validBidRequests) { - let eids; - const bidRequest = validBidRequests[0]; - if (bidRequest && bidRequest.userId) { - eids = bidRequest.userIdAsEids; - this.handleTTDId(eids, validBidRequests); - } - return eids; - }, - handleTTDId(eids, validBidRequests) { - let ttdId = null; - let adsrvrOrgId = config.getConfig('adsrvrOrgId'); - if (isStr(deepAccess(validBidRequests, '0.userId.tdid'))) { - ttdId = validBidRequests[0].userId.tdid; - } else if (adsrvrOrgId && isStr(adsrvrOrgId.TDID)) { - ttdId = adsrvrOrgId.TDID; - } - if (ttdId !== null) { - eids.push({ - 'source': 'adserver.org', - 'uids': [{ - 'id': ttdId, - 'atype': 1, - 'ext': { - 'rtiPartner': 'TDID' - } - }] - }); - } - }, getGetParametersAsObject() { - let items = location.search.substr(1).split('&'); - let ret = {}; - let tmp = null; - for (let index = 0; index < items.length; index++) { - tmp = items[index].split('='); - ret[tmp[0]] = tmp[1]; - } - return ret; + let parsed = parseUrl(getRefererInfo().page); + logInfo('getGetParametersAsObject found:', parsed.search); + return parsed.search; }, - /** - * Do we have to block this request? Could be due to config values (no longer checking gdpr) - * @return {boolean|*[]} true = block the request, else false - */ blockTheRequest() { let ozRequest = this.getWhitelabelConfigItem('ozone.oz_request'); if (typeof ozRequest == 'boolean' && !ozRequest) { - logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}one.${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); + logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); return true; } return false; }, - /** - * This returns a random ID for this page. It starts off with the current ms timestamp then appends a random component - * @return {string} - */ getPageId: function() { if (this.propertyBag.pageId == null) { let randPart = ''; @@ -829,14 +714,6 @@ export const spec = { ret = this._unpackVideoConfigIntoIABformat(ret, childConfig); return ret; }, - /** - * - * look in ONE object to get video config (we need to call this multiple times, so child settings override parent) - * @param ret - * @param objConfig - * @return {*} - * @private - */ _unpackVideoConfigIntoIABformat(ret, objConfig) { let arrVideoKeysAllowed = ['mimes', 'minduration', 'maxduration', 'protocols', 'w', 'h', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', 'delivery', 'pos', 'companionad', 'api', 'companiontype']; for (const key in objConfig) { @@ -865,14 +742,6 @@ export const spec = { objRet = this._addVideoDefaults(objRet, childConfig, true); // child config will override parent config return objRet; }, - /** - * modify objRet, adding in default values - * @param objRet - * @param objConfig - * @param addIfMissing - * @return {*} - * @private - */ _addVideoDefaults(objRet, objConfig, addIfMissing) { let context = deepAccess(objConfig, 'context'); if (context === 'outstream') { @@ -889,15 +758,38 @@ export const spec = { objRet.skip = skippable ? 1 : 0; } return objRet; + }, + getLoggableBidObject(bid) { + let logObj = { + ad: bid.ad, + adId: bid.adId, + adUnitCode: bid.adUnitCode, + adm: bid.adm, + adomain: bid.adomain, + adserverTargeting: bid.adserverTargeting, + auctionId: bid.auctionId, + bidId: bid.bidId, + bidder: bid.bidder, + bidderCode: bid.bidderCode, + cpm: bid.cpm, + creativeId: bid.creativeId, + crid: bid.crid, + currency: bid.currency, + h: bid.h, + w: bid.w, + impid: bid.impid, + mediaType: bid.mediaType, + params: bid.params, + price: bid.price, + transactionId: bid.transactionId, + ttl: bid.ttl + }; + if (bid.hasOwnProperty('floorData')) { + logObj.floorData = bid.floorData; + } + return logObj; } }; - -/** - * add a page-level-unique adId element to all server response bids. - * NOTE that this is destructive - it mutates the serverResponse object sent in as a parameter - * @param seatbid object (serverResponse.seatbid) - * @returns seatbid object - */ export function injectAdIdsIntoAllBidResponses(seatbid) { logInfo('injectAdIdsIntoAllBidResponses', seatbid); for (let i = 0; i < seatbid.length; i++) { @@ -908,7 +800,6 @@ export function injectAdIdsIntoAllBidResponses(seatbid) { } return seatbid; } - export function checkDeepArray(Arr) { if (Array.isArray(Arr)) { if (Array.isArray(Arr[0])) { @@ -920,7 +811,6 @@ export function checkDeepArray(Arr) { return Arr; } } - export function defaultSize(thebidObj) { if (!thebidObj) { logInfo('defaultSize received empty bid obj! going to return fixed default size'); @@ -935,13 +825,6 @@ export function defaultSize(thebidObj) { returnObject.defaultHeight = checkDeepArray(sizes)[1]; return returnObject; } - -/** - * Do the messy searching for the best bid response in the serverResponse.seatbid array matching the requestBid.bidId - * @param requestBid - * @param serverResponseSeatBid - * @returns {*} bid object - */ export function ozoneGetWinnerForRequestBid(requestBidId, serverResponseSeatBid) { let thisBidWinner = null; let winningSeat = null; @@ -960,13 +843,6 @@ export function ozoneGetWinnerForRequestBid(requestBidId, serverResponseSeatBid) } return {'seat': winningSeat, 'bid': thisBidWinner}; } - -/** - * Get a list of all the bids, for this bidId. The keys in the response object will be {seatname} OR {seatname}{w}x{h} if seatname already exists - * @param matchBidId - * @param serverResponseSeatBid - * @returns {} = {ozone|320x600:{obj}, ozone|320x250:{obj}, appnexus|300x250:{obj}, ... } - */ export function ozoneGetAllBidsForBidId(matchBidId, serverResponseSeatBid) { let objBids = {}; for (let j = 0; j < serverResponseSeatBid.length; j++) { @@ -986,21 +862,13 @@ export function ozoneGetAllBidsForBidId(matchBidId, serverResponseSeatBid) { } return objBids; } - -/** - * Round the bid price down according to the granularity - * @param price - * @param mediaType = video, banner or native - */ export function getRoundedBid(price, mediaType) { const mediaTypeGranularity = config.getConfig(`mediaTypePriceGranularity.${mediaType}`); // might be string or object or nothing; if set then this takes precedence over 'priceGranularity' let objBuckets = config.getConfig('customPriceBucket'); // this is always an object - {} if strBuckets is not 'custom' let strBuckets = config.getConfig('priceGranularity'); // priceGranularity value, always a string ** if priceGranularity is set to an object then it's always 'custom' ** let theConfigObject = getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets); let theConfigKey = getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets); - logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); - let priceStringsObj = getPriceBucketString( price, theConfigObject, @@ -1021,13 +889,6 @@ export function getRoundedBid(price, mediaType) { } return priceStringsObj['auto']; } - -/** - * return the key to use to get the value out of the priceStrings object, taking into account anything at - * config.priceGranularity level or config.mediaType.xxx level - * I've noticed that the key specified by prebid core : config.getConfig('priceGranularity') does not properly - * take into account the 2-levels of config - */ export function getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets) { if (typeof mediaTypeGranularity === 'string') { return mediaTypeGranularity; @@ -1040,11 +901,6 @@ export function getGranularityKeyName(mediaType, mediaTypeGranularity, strBucket } return 'auto'; // fall back to a default key - should literally never be needed. } - -/** - * return the object to use to create the custom value of the priceStrings object, taking into account anything at - * config.priceGranularity level or config.mediaType.xxx level - */ export function getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets) { if (typeof mediaTypeGranularity === 'object') { return mediaTypeGranularity; @@ -1054,12 +910,6 @@ export function getGranularityObject(mediaType, mediaTypeGranularity, strBuckets } return ''; } - -/** - * We expect to be able to find a standard set of properties on winning bid objects; add them here. - * @param seatBid - * @returns {*} - */ export function ozoneAddStandardProperties(seatBid, defaultWidth, defaultHeight) { seatBid.cpm = seatBid.price; seatBid.bidId = seatBid.impid; @@ -1073,12 +923,6 @@ export function ozoneAddStandardProperties(seatBid, defaultWidth, defaultHeight) seatBid.ttl = 300; return seatBid; } - -/** - * - * @param objVideo will be like {"playerSize":[640,480],"mimes":["video/mp4"],"context":"outstream"} or POSSIBLY {"playerSize":[[640,480]],"mimes":["video/mp4"],"context":"outstream"} - * @return object {w,h} or null - */ export function getWidthAndHeightFromVideoObject(objVideo) { let playerSize = getPlayerSizeFromObject(objVideo); if (!playerSize) { @@ -1098,11 +942,6 @@ export function getWidthAndHeightFromVideoObject(objVideo) { } return ({'w': playerSize[0], 'h': playerSize[1]}); } - -/** - * @param objVideo will be like {"playerSize":[640,480],"mimes":["video/mp4"],"context":"outstream"} or POSSIBLY {"playerSize":[[640,480]],"mimes":["video/mp4"],"context":"outstream"} - * @return object {w,h} or null - */ export function playerSizeIsNestedArray(objVideo) { let playerSize = getPlayerSizeFromObject(objVideo); if (!playerSize) { @@ -1113,12 +952,6 @@ export function playerSizeIsNestedArray(objVideo) { } return (playerSize[0] && typeof playerSize[0] === 'object'); } - -/** - * Common functionality when looking at a video object, to get the playerSize - * @param objVideo - * @returns {*} - */ function getPlayerSizeFromObject(objVideo) { logInfo('getPlayerSizeFromObject received object', objVideo); let playerSize = deepAccess(objVideo, 'playerSize'); @@ -1135,10 +968,6 @@ function getPlayerSizeFromObject(objVideo) { } return playerSize; } -/* - Rendering video ads - create a renderer instance, mark it as not loaded, set a renderer function. - The renderer function will not assume that the renderer script is loaded - it will push() the ultimate render function call - */ function newRenderer(adUnitCode, rendererOptions = {}) { let isLoaded = window.ozoneVideo; logInfo(`newRenderer going to set loaded to ${isLoaded ? 'true' : 'false'}`); @@ -1151,16 +980,18 @@ function newRenderer(adUnitCode, rendererOptions = {}) { try { renderer.setRender(outstreamRender); } catch (err) { - logError('Prebid Error when calling setRender on renderer', JSON.parse(JSON.stringify(renderer)), err); + logError('Prebid Error when calling setRender on renderer', renderer, err); } + logInfo('returning renderer object'); return renderer; } function outstreamRender(bid) { - logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid =', JSON.parse(JSON.stringify(bid))); + logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid = (first static, then reference)'); + logInfo(JSON.parse(JSON.stringify(spec.getLoggableBidObject(bid)))); bid.renderer.push(() => { + logInfo('Going to execute window.ozoneVideo.outstreamRender'); window.ozoneVideo.outstreamRender(bid); }); } - registerBidder(spec); logInfo(`*BidAdapter ${OZONEVERSION} was loaded`); diff --git a/modules/ozoneBidAdapter.md b/modules/ozoneBidAdapter.md index ca18c962219..6f4cf752f22 100644 --- a/modules/ozoneBidAdapter.md +++ b/modules/ozoneBidAdapter.md @@ -72,3 +72,40 @@ adUnits = [{ }] }]; ``` + +//Instream Video adUnit + +adUnits = [{ + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2] + } + }, + bids: [{ + bidder: 'ozone', + params: { + publisherId: 'OZONENUK0001', + placementId: '8000000328', // or 999 + siteId: '4204204201', + video: { + skippable: true, + playback_method: ['auto_play_sound_off'] + }, + customData: [{ + "settings": {}, + "targeting": { + "key": "value", + "key2": ["value1", "value2"] + } + } + ] + + } + }] + }; +``` diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index b29494e0b54..4c7e330b237 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -7,12 +7,6 @@ import * as utils from '../../../src/utils.js'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; -/* - -NOTE - use firefox console to deep copy the objects to use here - - */ -var originalPropertyBag = {'pageId': null}; var validBidRequests = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -147,7 +141,6 @@ var validBidRequestsWithUserIdData = [ }] } ] - } ]; var validBidRequestsMinimal = [ @@ -176,7 +169,6 @@ var validBidRequestsNoSizes = [ transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; - var validBidRequestsWithBannerMediaType = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -205,7 +197,6 @@ var validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo = [ transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; - var validBidRequests1OutstreamVideo2020 = [ { 'bidder': 'ozone', @@ -288,7 +279,6 @@ var validBidRequests1OutstreamVideo2020 = [ 'bidderWinsCount': 0 } ]; - var validBidderRequest1OutstreamVideo2020 = { bidderRequest: { auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -392,84 +382,79 @@ var validBidderRequest1OutstreamVideo2020 = { } }; var validBidderRequest = { - bidderRequest: { + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + auctionStart: 1536838908986, + bidderCode: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + bids: [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', - auctionStart: 1536838908986, - bidderCode: 'ozone', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - bids: [{ - adUnitCode: 'div-gpt-ad-1460505748561-0', - auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', - bidId: '2899ec066a91ff8', - bidRequestsCount: 1, - bidder: 'ozone', - bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, - sizes: [[300, 250], [300, 600]], - transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' - }], - doneCbCallCount: 1, - start: 1536838908987, - timeout: 3000 - } + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + }], + doneCbCallCount: 1, + start: 1536838908987, + timeout: 3000 }; - var bidderRequestWithFullGdpr = { - bidderRequest: { + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + auctionStart: 1536838908986, + bidderCode: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + bids: [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', - auctionStart: 1536838908986, - bidderCode: 'ozone', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - bids: [{ - adUnitCode: 'div-gpt-ad-1460505748561-0', - auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', - bidId: '2899ec066a91ff8', - bidRequestsCount: 1, - bidder: 'ozone', - bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, - sizes: [[300, 250], [300, 600]], - transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' - }], - doneCbCallCount: 1, - start: 1536838908987, - timeout: 3000, - gdprConsent: { - 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', - 'vendorData': { - 'metadata': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', - 'gdprApplies': true, - 'hasGlobalScope': false, - 'cookieVersion': '1', - 'created': '2019-05-31T12:46:48.825', - 'lastUpdated': '2019-05-31T12:46:48.825', - 'cmpId': '28', - 'cmpVersion': '1', - 'consentLanguage': 'en', - 'consentScreen': '1', - 'vendorListVersion': 148, - 'maxVendorId': 631, - 'purposeConsents': { - '1': true, - '2': true, - '3': true, - '4': true, - '5': true - }, - 'vendorConsents': { - '468': true, - '522': true, - '524': true, /* 524 is ozone */ - '565': true, - '591': true - } + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + }], + doneCbCallCount: 1, + start: 1536838908987, + timeout: 3000, + gdprConsent: { + 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + 'vendorData': { + 'metadata': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + 'gdprApplies': true, + 'hasGlobalScope': false, + 'cookieVersion': '1', + 'created': '2019-05-31T12:46:48.825', + 'lastUpdated': '2019-05-31T12:46:48.825', + 'cmpId': '28', + 'cmpVersion': '1', + 'consentLanguage': 'en', + 'consentScreen': '1', + 'vendorListVersion': 148, + 'maxVendorId': 631, + 'purposeConsents': { + '1': true, + '2': true, + '3': true, + '4': true, + '5': true }, - 'gdprApplies': true - }, } + 'vendorConsents': { + '468': true, + '522': true, + '524': true, /* 524 is ozone */ + '565': true, + '591': true + } + }, + 'gdprApplies': true + } }; - var gdpr1 = { 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', 'vendorData': { @@ -502,7 +487,6 @@ var gdpr1 = { }, 'gdprApplies': true }; - var bidderRequestWithPartialGdpr = { bidderRequest: { auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -547,7 +531,6 @@ var bidderRequestWithPartialGdpr = { } } }; - var validResponse = { 'body': { 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', @@ -604,7 +587,6 @@ var validResponse = { }, 'headers': {} }; - var validResponse2Bids = { 'body': { 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', @@ -691,9 +673,6 @@ var validResponse2Bids = { }, 'headers': {} }; -/* -A bidder returns a bid for both sizes in an adunit - */ var validResponse2BidsSameAdunit = { 'body': { 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', @@ -780,14 +759,6 @@ var validResponse2BidsSameAdunit = { }, 'headers': {} }; -/* - -SPECIAL CONSIDERATION FOR VIDEO TESTS: - -DO NOT USE _validVideoResponse directly - the interpretResponse function will modify it (adding a renderer!!!) so all -subsequent calls will already have a renderer attached!!! - -*/ function getCleanValidVideoResponse() { return JSON.parse(JSON.stringify(_validVideoResponse)); } @@ -873,7 +844,6 @@ var _validVideoResponse = { }, 'headers': {} }; - var validBidResponse1adWith2Bidders = { 'body': { 'id': '91221f96-b931-4acc-8f05-c2a1186fa5ac', @@ -964,11 +934,6 @@ var validBidResponse1adWith2Bidders = { }, 'headers': {} }; - -/* -testing 2 ads, 2 bidders, one bidder bids for both slots in one adunit - */ - var multiRequest1 = [ { 'bidder': 'ozone', @@ -1101,182 +1066,178 @@ var multiRequest1 = [ 'bidderWinsCount': 0 } ]; - var multiBidderRequest1 = { - bidderRequest: { - 'bidderCode': 'ozone', - 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', - 'bidderRequestId': '1d03a1dfc563fc', - 'bids': [ - { - 'bidder': 'ozone', - 'params': { - 'publisherId': 'OZONERUP0001', - 'siteId': '4204204201', - 'placementId': '0420420421', - 'customData': [ - { - 'settings': {}, - 'targeting': { - 'sens': 'f', - 'pt1': '/uk', - 'pt2': 'uk', - 'pt3': 'network-front', - 'pt4': 'ng', - 'pt5': [ - 'uk' - ], - 'pt7': 'desktop', - 'pt8': [ - 'tfmqxwj7q', - 'txeh7uyo0', - 't8nxz6qzd', - 't8nyiude5', - 'sek9ghqwi' - ], - 'pt9': '|k0xw2vqzp33kklb3j5w4|||' - } - } - ] - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 + 'bidderCode': 'ozone', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'bidderRequestId': '1d03a1dfc563fc', + 'bids': [ + { + 'bidder': 'ozone', + 'params': { + 'publisherId': 'OZONERUP0001', + 'siteId': '4204204201', + 'placementId': '0420420421', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' ], - [ - 300, - 600 - ] - ] + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'txeh7uyo0', + 't8nxz6qzd', + 't8nyiude5', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } } - }, - 'adUnitCode': 'mpu', - 'transactionId': '6480bac7-31b5-4723-9145-ad8966660651', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '2d30e86db743a8', - 'bidderRequestId': '1d03a1dfc563fc', - 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 + ] }, - { - 'bidder': 'ozone', - 'params': { - 'publisherId': 'OZONERUP0001', - 'siteId': '4204204201', - 'placementId': '0420420421', - 'customData': [ - { - 'settings': {}, - 'targeting': { - 'sens': 'f', - 'pt1': '/uk', - 'pt2': 'uk', - 'pt3': 'network-front', - 'pt4': 'ng', - 'pt5': [ - 'uk' - ], - 'pt7': 'desktop', - 'pt8': [ - 'tfmqxwj7q', - 'penl4dfdk', - 't8nxz6qzd', - 't8nyiude5', - 'sek9ghqwi' - ], - 'pt9': '|k0xw2vqzp33kklb3j5w4|||' - } - } - ] - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 728, - 90 - ], - [ - 970, - 250 - ] + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 ] - } - }, - 'adUnitCode': 'leaderboard', - 'transactionId': 'a49988e6-ae7c-46c4-9598-f18db49892a0', - 'sizes': [ - [ - 728, - 90 - ], - [ - 970, - 250 ] + } + }, + 'adUnitCode': 'mpu', + 'transactionId': '6480bac7-31b5-4723-9145-ad8966660651', + 'sizes': [ + [ + 300, + 250 ], - 'bidId': '3025f169863b7f8', - 'bidderRequestId': '1d03a1dfc563fc', - 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'auctionStart': 1592918645574, - 'timeout': 3000, - 'refererInfo': { - 'referer': 'http://ozone.ardm.io/adapter/2.4.0/620x350-switch.html?guardian=true&pbjs_debug=true', - 'reachedTop': true, - 'numIframes': 0, - 'stack': [ - 'http://ozone.ardm.io/adapter/2.4.0/620x350-switch.html?guardian=true&pbjs_debug=true' - ] + [ + 300, + 600 + ] + ], + 'bidId': '2d30e86db743a8', + 'bidderRequestId': '1d03a1dfc563fc', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 }, - 'gdprConsent': { - 'consentString': 'BOvy5sFO1dBa2AKAiBENDP-AAAAwVrv7_77-_9f-_f__9uj3Gr_v_f__32ccL5tv3h_7v-_7fi_-0nV4u_1tft9ydk1-5ctDztp507iakiPHmqNeb9n_mz1eZpRP58E09j53z7Ew_v8_v-b7BCPN_Y3v-8K96kA', - 'vendorData': { - 'metadata': 'BOvy5sFO1dBa2AKAiBENDPA', - 'gdprApplies': true, - 'hasGlobalConsent': false, - 'hasGlobalScope': false, - 'purposeConsents': { - '1': true, - '2': true, - '3': true, - '4': true, - '5': true - }, - 'vendorConsents': { - '1': true, - '2': true, - '3': false, - '4': true, - '5': true + { + 'bidder': 'ozone', + 'params': { + 'publisherId': 'OZONERUP0001', + 'siteId': '4204204201', + 'placementId': '0420420421', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' + ], + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'penl4dfdk', + 't8nxz6qzd', + 't8nyiude5', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } + } + ] + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 728, + 90 + ], + [ + 970, + 250 + ] + ] } }, - 'gdprApplies': true + 'adUnitCode': 'leaderboard', + 'transactionId': 'a49988e6-ae7c-46c4-9598-f18db49892a0', + 'sizes': [ + [ + 728, + 90 + ], + [ + 970, + 250 + ] + ], + 'bidId': '3025f169863b7f8', + 'bidderRequestId': '1d03a1dfc563fc', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1592918645574, + 'timeout': 3000, + 'refererInfo': { + 'referer': 'http://ozone.ardm.io/adapter/2.4.0/620x350-switch.html?guardian=true&pbjs_debug=true', + 'reachedTop': true, + 'numIframes': 0, + 'stack': [ + 'http://ozone.ardm.io/adapter/2.4.0/620x350-switch.html?guardian=true&pbjs_debug=true' + ] + }, + 'gdprConsent': { + 'consentString': 'BOvy5sFO1dBa2AKAiBENDP-AAAAwVrv7_77-_9f-_f__9uj3Gr_v_f__32ccL5tv3h_7v-_7fi_-0nV4u_1tft9ydk1-5ctDztp507iakiPHmqNeb9n_mz1eZpRP58E09j53z7Ew_v8_v-b7BCPN_Y3v-8K96kA', + 'vendorData': { + 'metadata': 'BOvy5sFO1dBa2AKAiBENDPA', + 'gdprApplies': true, + 'hasGlobalConsent': false, + 'hasGlobalScope': false, + 'purposeConsents': { + '1': true, + '2': true, + '3': true, + '4': true, + '5': true + }, + 'vendorConsents': { + '1': true, + '2': true, + '3': false, + '4': true, + '5': true + } }, - 'start': 1592918645578 - } + 'gdprApplies': true + }, + 'start': 1592918645578 }; - var multiResponse1 = { 'body': { 'id': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', @@ -1488,11 +1449,6 @@ var multiResponse1 = { }, 'headers': {} }; - -/* ---------------------end of 2 slots, 2 ---------------------------- - */ - describe('ozone Adapter', function () { describe('isBidRequestValid', function () { let validBidReq = { @@ -1503,13 +1459,10 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should return true when required params found', function () { expect(spec.isBidRequestValid(validBidReq)).to.equal(true); }); - var validBidReq2 = { - bidder: BIDDER_CODE, params: { placementId: '1310000099', @@ -1519,11 +1472,9 @@ describe('ozone Adapter', function () { }, siteId: 1234567890 } - it('should return true when required params found and all optional params are valid', function () { expect(spec.isBidRequestValid(validBidReq2)).to.equal(true); }); - var xEmptyPlacement = { bidder: BIDDER_CODE, params: { @@ -1532,11 +1483,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate empty placementId', function () { expect(spec.isBidRequestValid(xEmptyPlacement)).to.equal(false); }); - var xMissingPlacement = { bidder: BIDDER_CODE, params: { @@ -1544,11 +1493,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate missing placementId', function () { expect(spec.isBidRequestValid(xMissingPlacement)).to.equal(false); }); - var xBadPlacement = { bidder: BIDDER_CODE, params: { @@ -1557,11 +1504,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate placementId with a non-numeric value', function () { expect(spec.isBidRequestValid(xBadPlacement)).to.equal(false); }); - var xBadPlacementTooShort = { bidder: BIDDER_CODE, params: { @@ -1570,11 +1515,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate placementId with a numeric value of wrong length', function () { expect(spec.isBidRequestValid(xBadPlacementTooShort)).to.equal(false); }); - var xBadPlacementTooLong = { bidder: BIDDER_CODE, params: { @@ -1583,11 +1526,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate placementId with a numeric value of wrong length', function () { expect(spec.isBidRequestValid(xBadPlacementTooLong)).to.equal(false); }); - var xMissingPublisher = { bidder: BIDDER_CODE, params: { @@ -1595,11 +1536,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate missing publisherId', function () { expect(spec.isBidRequestValid(xMissingPublisher)).to.equal(false); }); - var xMissingSiteId = { bidder: BIDDER_CODE, params: { @@ -1607,11 +1546,9 @@ describe('ozone Adapter', function () { placementId: '1234567890', } }; - it('should not validate missing sitetId', function () { expect(spec.isBidRequestValid(xMissingSiteId)).to.equal(false); }); - var xBadPublisherTooShort = { bidder: BIDDER_CODE, params: { @@ -1620,11 +1557,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate publisherId being too short', function () { expect(spec.isBidRequestValid(xBadPublisherTooShort)).to.equal(false); }); - var xBadPublisherTooLong = { bidder: BIDDER_CODE, params: { @@ -1633,11 +1568,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate publisherId being too long', function () { expect(spec.isBidRequestValid(xBadPublisherTooLong)).to.equal(false); }); - var publisherNumericOk = { bidder: BIDDER_CODE, params: { @@ -1646,11 +1579,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should validate publisherId being 12 digits', function () { expect(spec.isBidRequestValid(publisherNumericOk)).to.equal(true); }); - var xEmptyPublisher = { bidder: BIDDER_CODE, params: { @@ -1659,11 +1590,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate empty publisherId', function () { expect(spec.isBidRequestValid(xEmptyPublisher)).to.equal(false); }); - var xBadSite = { bidder: BIDDER_CODE, params: { @@ -1672,37 +1601,15 @@ describe('ozone Adapter', function () { siteId: '12345Z' } }; - it('should not validate bad siteId', function () { expect(spec.isBidRequestValid(xBadSite)).to.equal(false); }); - - var xBadSiteTooLong = { - bidder: BIDDER_CODE, - params: { - placementId: '1234567890', - publisherId: '9876abcd12-3', - siteId: '12345678901' - } - }; - it('should not validate siteId too long', function () { expect(spec.isBidRequestValid(xBadSite)).to.equal(false); }); - - var xBadSiteTooShort = { - bidder: BIDDER_CODE, - params: { - placementId: '1234567890', - publisherId: '9876abcd12-3', - siteId: '123456789' - } - }; - it('should not validate siteId too short', function () { expect(spec.isBidRequestValid(xBadSite)).to.equal(false); }); - var allNonStrings = { bidder: BIDDER_CODE, params: { @@ -1711,11 +1618,9 @@ describe('ozone Adapter', function () { siteId: 1234567890 } }; - it('should validate all numeric values being sent as non-string numbers', function () { expect(spec.isBidRequestValid(allNonStrings)).to.equal(true); }); - var emptySiteId = { bidder: BIDDER_CODE, params: { @@ -1724,11 +1629,9 @@ describe('ozone Adapter', function () { siteId: '' } }; - it('should not validate siteId being empty string (it is required now)', function () { expect(spec.isBidRequestValid(emptySiteId)).to.equal(false); }); - var xBadCustomData = { bidder: BIDDER_CODE, params: { @@ -1738,12 +1641,10 @@ describe('ozone Adapter', function () { 'customData': 'this aint gonna work' } }; - it('should not validate customData not being an array', function () { expect(spec.isBidRequestValid(xBadCustomData)).to.equal(false); }); - - var xBadCustomData_OLD_CUSTOMDATA_VALUE = { + var xBadCustomDataOldCustomdataValue = { bidder: BIDDER_CODE, params: { 'placementId': '1234567890', @@ -1752,12 +1653,10 @@ describe('ozone Adapter', function () { 'customData': {'gender': 'bart', 'age': 'low'} } }; - it('should not validate customData being an object, not an array', function () { - expect(spec.isBidRequestValid(xBadCustomData_OLD_CUSTOMDATA_VALUE)).to.equal(false); + expect(spec.isBidRequestValid(xBadCustomDataOldCustomdataValue)).to.equal(false); }); - - var xBadCustomData_zerocd = { + var xBadCustomDataZerocd = { bidder: BIDDER_CODE, params: { 'placementId': '1111111110', @@ -1766,12 +1665,10 @@ describe('ozone Adapter', function () { 'customData': [] } }; - it('should not validate customData array having no elements', function () { - expect(spec.isBidRequestValid(xBadCustomData_zerocd)).to.equal(false); + expect(spec.isBidRequestValid(xBadCustomDataZerocd)).to.equal(false); }); - - var xBadCustomData_notargeting = { + var xBadCustomDataNotargeting = { bidder: BIDDER_CODE, params: { 'placementId': '1234567890', @@ -1781,10 +1678,9 @@ describe('ozone Adapter', function () { } }; it('should not validate customData[] having no "targeting"', function () { - expect(spec.isBidRequestValid(xBadCustomData_notargeting)).to.equal(false); + expect(spec.isBidRequestValid(xBadCustomDataNotargeting)).to.equal(false); }); - - var xBadCustomData_tgt_not_obj = { + var xBadCustomDataTgtNotObj = { bidder: BIDDER_CODE, params: { 'placementId': '1234567890', @@ -1794,9 +1690,8 @@ describe('ozone Adapter', function () { } }; it('should not validate customData[0].targeting not being an object', function () { - expect(spec.isBidRequestValid(xBadCustomData_tgt_not_obj)).to.equal(false); + expect(spec.isBidRequestValid(xBadCustomDataTgtNotObj)).to.equal(false); }); - var xBadCustomParams = { bidder: BIDDER_CODE, params: { @@ -1821,11 +1716,9 @@ describe('ozone Adapter', function () { mimes: ['video/mp4']} } }; - it('should not validate video without context attribute', function () { expect(spec.isBidRequestValid(xBadVideoContext2)).to.equal(false); }); - let validVideoBidReq = { bidder: BIDDER_CODE, params: { @@ -1839,7 +1732,6 @@ describe('ozone Adapter', function () { 'context': 'outstream'}, } }; - it('should validate video outstream being sent', function () { expect(spec.isBidRequestValid(validVideoBidReq)).to.equal(true); }); @@ -1849,47 +1741,44 @@ describe('ozone Adapter', function () { expect(spec.isBidRequestValid(instreamVid)).to.equal(true); }); }); - describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig() + }); it('sends bid request to OZONEURI via POST', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.url).to.equal(OZONEURI); expect(request.method).to.equal('POST'); }); - it('sends data as a string', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.data).to.be.a('string'); }); - it('sends all bid parameters', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('adds all parameters inside the ext object only', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(request).not.to.have.key('lotameData'); expect(request).not.to.have.key('customData'); }); - it('adds all parameters inside the ext object only - lightning', function () { let localBidReq = JSON.parse(JSON.stringify(validBidRequests)); - const request = spec.buildRequests(localBidReq, validBidderRequest.bidderRequest); + const request = spec.buildRequests(localBidReq, validBidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(request).not.to.have.key('lotameData'); expect(request).not.to.have.key('customData'); }); - it('ignores ozoneData in & after version 2.1.1', function () { let validBidRequestsWithOzoneData = JSON.parse(JSON.stringify(validBidRequests)); validBidRequestsWithOzoneData[0].params.ozoneData = {'networkID': '3048', 'dfpSiteID': 'd.thesun', 'sectionID': 'homepage', 'path': '/', 'sec_id': 'null', 'sec': 'sec', 'topics': 'null', 'kw': 'null', 'aid': 'null', 'search': 'null', 'article_type': 'null', 'hide_ads': '', 'article_slug': 'null'}; - const request = spec.buildRequests(validBidRequestsWithOzoneData, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsWithOzoneData, validBidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); @@ -1897,43 +1786,36 @@ describe('ozone Adapter', function () { expect(request).not.to.have.key('lotameData'); expect(request).not.to.have.key('customData'); }); - it('has correct bidder', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.bidderRequest.bids[0].bidder).to.equal(BIDDER_CODE); }); - it('handles mediaTypes element correctly', function () { - const request = spec.buildRequests(validBidRequestsWithBannerMediaType, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsWithBannerMediaType, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('handles no ozone or custom data', function () { - const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('handles video mediaType element correctly, with outstream video', function () { - const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('should not crash when there is no sizes element at all', function () { - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('should be able to handle non-single requests', function () { config.setConfig({'ozone': {'singleRequest': false}}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); expect(request).to.be.a('array'); expect(request[0]).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); config.setConfig({'ozone': {'singleRequest': true}}); }); - it('should add gdpr consent information to the request when ozone is true', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; - let bidderRequest = validBidderRequest.bidderRequest; + let bidderRequest = validBidderRequest; bidderRequest.gdprConsent = { consentString: consentString, gdprApplies: true, @@ -1944,16 +1826,14 @@ describe('ozone Adapter', function () { purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} } } - const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); const payload = JSON.parse(request.data); expect(payload.regs.ext.gdpr).to.equal(1); expect(payload.user.ext.consent).to.equal(consentString); }); - it('should add gdpr consent information to the request when vendorData is missing vendorConsents (Mirror)', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; - let bidderRequest = validBidderRequest.bidderRequest; + let bidderRequest = validBidderRequest; bidderRequest.gdprConsent = { consentString: consentString, gdprApplies: true, @@ -1962,16 +1842,14 @@ describe('ozone Adapter', function () { gdprApplies: true } } - const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); const payload = JSON.parse(request.data); expect(payload.regs.ext.gdpr).to.equal(1); expect(payload.user.ext.consent).to.equal(consentString); }); - it('should set regs.ext.gdpr flag to 0 when gdprApplies is false', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; - let bidderRequest = validBidderRequest.bidderRequest; + let bidderRequest = validBidderRequest; bidderRequest.gdprConsent = { consentString: consentString, gdprApplies: false, @@ -1982,15 +1860,13 @@ describe('ozone Adapter', function () { purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} } }; - const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); const payload = JSON.parse(request.data); expect(payload.regs.ext.gdpr).to.equal(0); }); - it('should not have imp[N].ext.ozone.userId', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; - let bidderRequest = validBidderRequest.bidderRequest; + let bidderRequest = validBidderRequest; bidderRequest.gdprConsent = { consentString: consentString, gdprApplies: false, @@ -2001,7 +1877,6 @@ describe('ozone Adapter', function () { purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} } }; - let bidRequests = validBidRequests; bidRequests[0]['userId'] = { 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, @@ -2019,7 +1894,6 @@ describe('ozone Adapter', function () { expect(firstBid).to.not.have.property('userId'); delete validBidRequests[0].userId; // tidy up now, else it will screw with other tests }); - it('should pick up the value of pubcid when built using the pubCommonId module (not userId)', function () { let bidRequests = validBidRequests; bidRequests[0]['userId'] = { @@ -2031,23 +1905,13 @@ describe('ozone Adapter', function () { 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} }; bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; - const request = spec.buildRequests(bidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(bidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.pubcid).to.equal(bidRequests[0]['crumbs']['pubcid']); delete validBidRequests[0].userId; // tidy up now, else it will screw with other tests }); - it('should add a user.ext.eids object to contain user ID data in the new location (Nov 2019) Updated Aug 2020', function() { - const request = spec.buildRequests(validBidRequestsWithUserIdData, validBidderRequest.bidderRequest); - /* - 'pubcid': '12345678', - 'tdid': '1111tdid', - 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, - 'criteoId': '1111criteoId', - 'idl_env': 'liverampId', - 'parrableId': {'eid': '01.5678.parrableid'} - */ - + const request = spec.buildRequests(validBidRequestsWithUserIdData, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.user).to.exist; expect(payload.user.ext).to.exist; @@ -2067,12 +1931,11 @@ describe('ozone Adapter', function () { expect(payload.user.ext.eids[6]['source']).to.equal('parrableId'); expect(payload.user.ext.eids[6]['uids'][0]['id']['eid']).to.equal('01.5678.parrableid'); }); - it('replaces the auction url for a config override', function () { spec.propertyBag.whitelabel = null; let fakeOrigin = 'http://sometestendpoint'; config.setConfig({'ozone': {'endpointOverride': {'origin': fakeOrigin}}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.url).to.equal(fakeOrigin + '/openrtb2/auction'); expect(request.method).to.equal('POST'); const data = JSON.parse(request.data); @@ -2080,12 +1943,11 @@ describe('ozone Adapter', function () { config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); spec.propertyBag.whitelabel = null; }); - it('replaces the FULL auction url for a config override', function () { spec.propertyBag.whitelabel = null; let fakeurl = 'http://sometestendpoint/myfullurl'; config.setConfig({'ozone': {'endpointOverride': {'auctionUrl': fakeurl}}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.url).to.equal(fakeurl); expect(request.method).to.equal('POST'); const data = JSON.parse(request.data); @@ -2093,7 +1955,6 @@ describe('ozone Adapter', function () { config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); spec.propertyBag.whitelabel = null; }); - it('replaces the renderer url for a config override', function () { spec.propertyBag.whitelabel = null; let fakeUrl = 'http://renderer.com'; @@ -2107,9 +1968,8 @@ describe('ozone Adapter', function () { spec.propertyBag.whitelabel = null; }); it('should generate all the adservertargeting keys correctly named', function () { - var specMock = utils.deepClone(spec); config.setConfig({'ozone': {'kvpPrefix': 'xx'}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(result[0].adserverTargeting).to.have.own.property('xx_appnexus_crid'); expect(utils.deepAccess(result[0].adserverTargeting, 'xx_appnexus_crid')).to.equal('98493581'); @@ -2118,33 +1978,28 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[0].adserverTargeting, 'xx_size')).to.equal('300x600'); expect(utils.deepAccess(result[0].adserverTargeting, 'xx_pb_r')).to.equal('0.50'); expect(utils.deepAccess(result[0].adserverTargeting, 'xx_bid')).to.equal('true'); - config.resetConfig(); }); it('should create a meta object on each bid returned', function () { - var specMock = utils.deepClone(spec); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(result[0]).to.have.own.property('meta'); expect(result[0].meta.advertiserDomains[0]).to.equal('http://prebid.org'); - config.resetConfig(); }); - it('replaces the kvp prefix ', function () { spec.propertyBag.whitelabel = null; config.setConfig({'ozone': {'kvpPrefix': 'test'}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone).to.haveOwnProperty('test_rw'); config.setConfig({'ozone': {'kvpPrefix': null}}); spec.propertyBag.whitelabel = null; }); - it('handles an alias ', function () { spec.propertyBag.whitelabel = null; config.setConfig({'lmc': {'kvpPrefix': 'test'}}); let br = JSON.parse(JSON.stringify(validBidRequests)); br[0]['bidder'] = 'lmc'; - const request = spec.buildRequests(br, validBidderRequest.bidderRequest); + const request = spec.buildRequests(br, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.lmc).to.haveOwnProperty('test_rw'); config.setConfig({'lmc': {'kvpPrefix': null}}); // I cant remove the key so set the value to null @@ -2155,7 +2010,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); @@ -2165,7 +2020,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {ozf: '1', ozpf: '0', ozrp: '2', ozip: '123'}; }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone.ozf).to.equal(1); expect(data.ext.ozone.ozpf).to.equal(0); @@ -2177,7 +2032,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {ozf: 'false', ozpf: 'true', ozrp: 'xyz', ozip: 'hello'}; }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone.ozf).to.equal(0); expect(data.ext.ozone.ozpf).to.equal(1); @@ -2189,7 +2044,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; - const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); @@ -2199,17 +2054,16 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {}; }; - let request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + let request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); let url = request.url; expect(url).to.equal('https://elb.the-ozone-project.com/openrtb2/auction'); let cookieUrl = specMock.getCookieSyncUrl(); expect(cookieUrl).to.equal('https://elb.the-ozone-project.com/static/load-cookie.html'); - specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'auction': 'dev', 'cookiesync': 'dev'}; }; - request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); url = request.url; expect(url).to.equal('https://test.ozpr.net/openrtb2/auction'); cookieUrl = specMock.getCookieSyncUrl(); @@ -2220,7 +2074,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': '1122334455'}; // 10 digits are valid }; - const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone.oz_rw).to.equal(1); expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1122334455'); @@ -2230,71 +2084,65 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': 'BADVAL'}; // 10 digits are valid }; - const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone.oz_rw).to.equal(0); expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1310000099'); }); - it('should pick up the config value of coppa & set it in the request', function () { config.setConfig({'coppa': true}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.regs).to.include.keys('coppa'); expect(payload.regs.coppa).to.equal(1); - config.resetConfig(); }); it('should pick up the config value of coppa & only set it in the request if its true', function () { config.setConfig({'coppa': false}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'regs.coppa')).to.be.undefined; - config.resetConfig(); }); it('should handle oz_omp_floor correctly', function () { config.setConfig({'ozone': {'oz_omp_floor': 1.56}}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.oz_omp_floor')).to.equal(1.56); - config.resetConfig(); }); it('should ignore invalid oz_omp_floor values', function () { config.setConfig({'ozone': {'oz_omp_floor': '1.56'}}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.oz_omp_floor')).to.be.undefined; - config.resetConfig(); }); it('should should contain a unique page view id in the auction request which persists across calls', function () { - let request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + let request = spec.buildRequests(validBidRequests, validBidderRequest); let payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.pv')).to.be.a('string'); - request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest.bidderRequest); + request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest); let payload2 = JSON.parse(request.data); expect(utils.deepAccess(payload2, 'ext.ozone.pv')).to.be.a('string'); expect(utils.deepAccess(payload2, 'ext.ozone.pv')).to.equal(utils.deepAccess(payload, 'ext.ozone.pv')); }); it('should indicate that the whitelist was used when it contains valid data', function () { config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_ozappnexus_pb', 'oz_ozappnexus_imp_id']}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(1); - config.resetConfig(); }); it('should indicate that the whitelist was not used when it contains no data', function () { config.setConfig({'ozone': {'oz_whitelist_adserver_keys': []}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(0); - config.resetConfig(); }); it('should indicate that the whitelist was not used when it is not set in the config', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(0); }); it('should handle ortb2 site data', function () { - const ortb2 = { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { 'site': { 'name': 'example_ortb2_name', 'domain': 'page.example.com', @@ -2307,14 +2155,14 @@ describe('ozone Adapter', function () { 'search': 'drill' } }; - const request = spec.buildRequests(validBidRequests, {...validBidderRequest.bidderRequest, ortb2}); + const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.imp[0].ext.ozone.customData[0].targeting.name).to.equal('example_ortb2_name'); expect(payload.user.ext).to.not.have.property('gender'); - config.resetConfig(); }); it('should add ortb2 site data when there is no customData already created', function () { - const ortb2 = { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { 'site': { 'name': 'example_ortb2_name', 'domain': 'page.example.com', @@ -2327,25 +2175,25 @@ describe('ozone Adapter', function () { 'search': 'drill' } }; - const request = spec.buildRequests(validBidRequestsNoCustomData, {...validBidderRequest.bidderRequest, ortb2}); + const request = spec.buildRequests(validBidRequestsNoCustomData, bidderRequest); const payload = JSON.parse(request.data); expect(payload.imp[0].ext.ozone.customData[0].targeting.name).to.equal('example_ortb2_name'); expect(payload.imp[0].ext.ozone.customData[0].targeting).to.not.have.property('gender') - config.resetConfig(); }); it('should add ortb2 user data to the user object', function () { - const ortb2 = { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { 'user': { - 'gender': 'who knows these days' + 'gender': 'I identify as a box of rocks' } }; - const request = spec.buildRequests(validBidRequests, {...validBidderRequest.bidderRequest, ortb2}); + const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); - expect(payload.user.gender).to.equal('who knows these days'); - config.resetConfig(); + expect(payload.user.gender).to.equal('I identify as a box of rocks'); }); it('should not override the user.ext.consent string even if this is set in config ortb2', function () { - const ortb2 = { + let bidderRequest = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr)); + bidderRequest.ortb2 = { 'user': { 'ext': { 'consent': 'this is the consent override that shouldnt work', @@ -2353,15 +2201,14 @@ describe('ozone Adapter', function () { } } }; - const request = spec.buildRequests(validBidRequests, {...bidderRequestWithFullGdpr.bidderRequest, ortb2}); + const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.user.ext.consent2).to.equal('this should be set'); expect(payload.user.ext.consent).to.equal('BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); - config.resetConfig(); }); it('should have openrtb video params', function() { let allowed = ['mimes', 'minduration', 'maxduration', 'protocols', 'w', 'h', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', 'delivery', 'pos', 'companionad', 'api', 'companiontype', 'ext']; - const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest); const payload = JSON.parse(request.data); const vid = (payload.imp[0].video); const keys = Object.keys(vid); @@ -2391,13 +2238,11 @@ describe('ozone Adapter', function () { }); let localBidRequest = JSON.parse(JSON.stringify(validBidRequestsWithBannerMediaType)); localBidRequest[0].getFloor = function(x) { return {'currency': 'USD', 'floor': 0.8} }; - const request = spec.buildRequests(localBidRequest, validBidderRequest.bidderRequest); + const request = spec.buildRequests(localBidRequest, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'imp.0.floor.banner.currency')).to.equal('USD'); expect(utils.deepAccess(payload, 'imp.0.floor.banner.floor')).to.equal(0.8); - config.resetConfig(); }); - it('handles schain object in each bidrequest (will be the same in each br)', function () { let br = JSON.parse(JSON.stringify(validBidRequests)); let schainConfigObject = { @@ -2412,44 +2257,44 @@ describe('ozone Adapter', function () { ] }; br[0]['schain'] = schainConfigObject; - const request = spec.buildRequests(br, validBidderRequest.bidderRequest); + const request = spec.buildRequests(br, validBidderRequest); const data = JSON.parse(request.data); expect(data.source.ext).to.haveOwnProperty('schain'); expect(data.source.ext.schain).to.deep.equal(schainConfigObject); // .deep.equal() : Target object deeply (but not strictly) equals `{a: 1}` }); }); - describe('interpretResponse', function () { + beforeEach(function () { + config.resetConfig() + }) it('should build bid array', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(result.length).to.equal(1); }); - it('should have all relevant fields', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); const bid = result[0]; expect(bid.cpm).to.equal(validResponse.body.seatbid[0].bid[0].cpm); expect(bid.width).to.equal(validResponse.body.seatbid[0].bid[0].width); expect(bid.height).to.equal(validResponse.body.seatbid[0].bid[0].height); }); - it('should build bid array with gdpr', function () { - let validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr.bidderRequest)); + let validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr)); validBR.gdprConsent = {'gdprApplies': 1, 'consentString': 'This is the gdpr consent string'}; const request = spec.buildRequests(validBidRequests, validBR); // works the old way, with GDPR not enforced by default const result = spec.interpretResponse(validResponse, request); expect(result.length).to.equal(1); }); it('should build bid array with usp/CCPA', function () { - let validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr.bidderRequest)); + let validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr)); validBR.uspConsent = '1YNY'; const request = spec.buildRequests(validBidRequests, validBR); const payload = JSON.parse(request.data); - expect(payload.user.ext.uspConsent).to.equal('1YNY'); + expect(payload.user.ext.uspConsent).not.to.exist; + expect(payload.regs.ext.us_privacy).to.equal('1YNY'); }); - it('should build bid array with only partial gdpr', function () { var validBidderRequestWithGdpr = bidderRequestWithPartialGdpr.bidderRequest; validBidderRequestWithGdpr.gdprConsent = {'gdprApplies': 1, 'consentString': 'This is the gdpr consent string'}; @@ -2457,26 +2302,22 @@ describe('ozone Adapter', function () { const payload = JSON.parse(request.data); expect(payload.user.ext.consent).to.be.a('string'); }); - it('should fail ok if no seatbid in server response', function () { const result = spec.interpretResponse({}, {}); expect(result).to.be.an('array'); expect(result).to.be.empty; }); - it('should fail ok if seatbid is not an array', function () { const result = spec.interpretResponse({'body': {'seatbid': 'nothing_here'}}, {}); expect(result).to.be.an('array'); expect(result).to.be.empty; }); - it('should have video renderer for outstream video', function () { const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest1OutstreamVideo2020.bidderRequest); const result = spec.interpretResponse(getCleanValidVideoResponse(), validBidderRequest1OutstreamVideo2020); const bid = result[0]; expect(bid.renderer).to.be.an.instanceOf(Renderer); }); - it('should have NO video renderer for instream video', function () { let instreamRequestsObj = JSON.parse(JSON.stringify(validBidRequests1OutstreamVideo2020)); instreamRequestsObj[0].mediaTypes.video.context = 'instream'; @@ -2487,99 +2328,87 @@ describe('ozone Adapter', function () { const bid = result[0]; expect(bid.hasOwnProperty('renderer')).to.be.false; }); - it('should correctly parse response where there are more bidders than ad slots', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validBidResponse1adWith2Bidders, request); expect(result.length).to.equal(2); }); - it('should have a ttl of 600', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(result[0].ttl).to.equal(300); }); - it('should handle oz_omp_floor_dollars correctly, inserting 1 as necessary', function () { config.setConfig({'ozone': {'oz_omp_floor': 0.01}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_omp')).to.equal('1'); - config.resetConfig(); }); it('should handle oz_omp_floor_dollars correctly, inserting 0 as necessary', function () { config.setConfig({'ozone': {'oz_omp_floor': 2.50}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_omp')).to.equal('0'); - config.resetConfig(); }); it('should handle missing oz_omp_floor_dollars correctly, inserting nothing', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_omp')).to.be.undefined; }); it('should handle ext.bidder.ozone.floor correctly, setting flr & rid as necessary', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); vres.body.seatbid[0].bid[0].ext.bidder.ozone = {floor: 1, ruleId: 'ZjbsYE1q'}; const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(1); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal('ZjbsYE1q'); - config.resetConfig(); }); it('should handle ext.bidder.ozone.floor correctly, inserting 0 as necessary', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); vres.body.seatbid[0].bid[0].ext.bidder.ozone = {floor: 0, ruleId: 'ZjbXXE1q'}; const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(0); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal('ZjbXXE1q'); - config.resetConfig(); }); it('should handle ext.bidder.ozone.floor correctly, inserting nothing as necessary', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); vres.body.seatbid[0].bid[0].ext.bidder.ozone = {}; const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr', null)).to.equal(null); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid', null)).to.equal(null); - config.resetConfig(); }); it('should handle ext.bidder.ozone.floor correctly, when bidder.ozone is not there', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr', null)).to.equal(null); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid', null)).to.equal(null); - config.resetConfig(); }); it('should handle a valid whitelist, removing items not on the list & leaving others', function () { config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_appnexus_crid', 'oz_appnexus_adId']}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adId')).to.equal('2899ec066a91ff8-0-oz-0'); - config.resetConfig(); }); it('should ignore a whitelist if enhancedAdserverTargeting is false', function () { config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_appnexus_crid', 'oz_appnexus_imp_id'], 'enhancedAdserverTargeting': false}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_imp_id')).to.be.undefined; - config.resetConfig(); }); it('should correctly handle enhancedAdserverTargeting being false', function () { config.setConfig({'ozone': {'enhancedAdserverTargeting': false}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_imp_id')).to.be.undefined; - config.resetConfig(); }); it('should add flr into ads request if floor exists in the auction response', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2Bids)); validres.body.seatbid[0].bid[0].ext.bidder.ozone = {'floor': 1}; const result = spec.interpretResponse(validres, request); @@ -2587,7 +2416,7 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_flr', '')).to.equal(''); }); it('should add rid into ads request if ruleId exists in the auction response', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2Bids)); validres.body.seatbid[0].bid[0].ext.bidder.ozone = {'ruleId': 123}; const result = spec.interpretResponse(validres, request); @@ -2595,13 +2424,13 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_rid', '')).to.equal(''); }); it('should add oz_ozappnexus_sid (cid value) for all appnexus bids', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2BidsSameAdunit)); const result = spec.interpretResponse(validres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_ozappnexus_sid')).to.equal(result[0].cid); }); it('should add unique adId values to each bid', function() { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2BidsSameAdunit)); const result = spec.interpretResponse(validres, request); expect(result.length).to.equal(1); @@ -2610,7 +2439,7 @@ describe('ozone Adapter', function () { }); it('should correctly process an auction with 2 adunits & multiple bidders one of which bids for both adslots', function() { let validres = JSON.parse(JSON.stringify(multiResponse1)); - let request = spec.buildRequests(multiRequest1, multiBidderRequest1.bidderRequest); + let request = spec.buildRequests(multiRequest1, multiBidderRequest1); let result = spec.interpretResponse(validres, request); expect(result.length).to.equal(4); // one of the 5 bids will have been removed expect(result[1]['price']).to.equal(0.521); @@ -2620,15 +2449,28 @@ describe('ozone Adapter', function () { validres = JSON.parse(JSON.stringify(multiResponse1)); validres.body.seatbid[0].bid[1].price = 1.1; validres.body.seatbid[0].bid[1].cpm = 1.1; - request = spec.buildRequests(multiRequest1, multiBidderRequest1.bidderRequest); + request = spec.buildRequests(multiRequest1, multiBidderRequest1); result = spec.interpretResponse(validres, request); expect(result[1]['price']).to.equal(1.1); expect(result[1]['impid']).to.equal('3025f169863b7f8'); expect(result[1]['id']).to.equal('18552976939844681'); expect(result[1]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-oz-1'); }); + it('should add mediaType: banner for a banner ad', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(result[0].mediaType).to.equal('banner'); + }); + it('should add mediaType: video for a video ad', function () { + let instreamRequestsObj = JSON.parse(JSON.stringify(validBidRequests1OutstreamVideo2020)); + instreamRequestsObj[0].mediaTypes.video.context = 'instream'; + let instreamBidderReq = JSON.parse(JSON.stringify(validBidderRequest1OutstreamVideo2020)); + instreamBidderReq.bidderRequest.bids[0].mediaTypes.video.context = 'instream'; + const result = spec.interpretResponse(getCleanValidVideoResponse(), instreamBidderReq); + const bid = result[0]; + expect(bid.mediaType).to.equal('video'); + }); }); - describe('userSyncs', function () { it('should fail gracefully if no server response', function () { const result = spec.getUserSyncs('bad', false, gdpr1); @@ -2639,7 +2481,7 @@ describe('ozone Adapter', function () { expect(result).to.be.empty; }); it('should append the various values if they exist', function() { - spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1); expect(result).to.be.an('array'); expect(result[0].url).to.include('publisherId=9876abcd12-3'); @@ -2648,19 +2490,18 @@ describe('ozone Adapter', function () { expect(result[0].url).to.include('gdpr_consent=BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); }); it('should append ccpa (usp data)', function() { - spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1, '1YYN'); expect(result).to.be.an('array'); expect(result[0].url).to.include('usp_consent=1YYN'); }); it('should use "" if no usp is sent to cookieSync', function() { - spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1); expect(result).to.be.an('array'); expect(result[0].url).to.include('usp_consent=&'); }); }); - describe('video object utils', function () { it('should find width & height from video object', function () { let obj = {'playerSize': [640, 480], 'mimes': ['video/mp4'], 'context': 'outstream'}; @@ -2715,7 +2556,7 @@ describe('ozone Adapter', function () { expect(result).to.be.null; }); it('should add oz_appnexus_dealid into ads request if dealid exists in the auction response', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2Bids)); validres.body.seatbid[0].bid[0].dealid = '1234'; const result = spec.interpretResponse(validres, request); @@ -2723,7 +2564,6 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_dealid', '')).to.equal(''); }); }); - describe('default size', function () { it('should should return default sizes if no obj is sent', function () { let obj = ''; @@ -2732,7 +2572,6 @@ describe('ozone Adapter', function () { expect(result.defaultWidth).to.equal(300); }); }); - describe('getGranularityKeyName', function() { it('should return a string granularity as-is', function() { const result = getGranularityKeyName('', 'this is it', ''); @@ -2747,7 +2586,6 @@ describe('ozone Adapter', function () { expect(result).to.equal('string buckets'); }); }); - describe('getGranularityObject', function() { it('should return an object as-is', function() { const result = getGranularityObject('', {'name': 'mark'}, '', ''); @@ -2758,19 +2596,19 @@ describe('ozone Adapter', function () { expect(result.name).to.equal('rupert'); }); }); - describe('blockTheRequest', function() { + beforeEach(function () { + config.resetConfig() + }) it('should return true if oz_request is false', function() { config.setConfig({'ozone': {'oz_request': false}}); - let result = spec.blockTheRequest(bidderRequestWithFullGdpr); + let result = spec.blockTheRequest(); expect(result).to.be.true; - config.resetConfig(); }); it('should return false if oz_request is true', function() { config.setConfig({'ozone': {'oz_request': true}}); - let result = spec.blockTheRequest(bidderRequestWithFullGdpr); + let result = spec.blockTheRequest(); expect(result).to.be.false; - config.resetConfig(); }); }); describe('getPageId', function() { @@ -2876,11 +2714,10 @@ describe('ozone Adapter', function () { expect(response[1].bid.length).to.equal(2); }); }); - /** - * spec.getWhitelabelConfigItem test - get a config value for a whitelabelled bidder, - * from a standard ozone.oz_xxxx_yyy string - */ describe('getWhitelabelConfigItem', function() { + beforeEach(function () { + config.resetConfig() + }) it('should fetch the whitelabelled equivalent config value correctly', function () { var specMock = utils.deepClone(spec); config.setConfig({'ozone': {'oz_omp_floor': 'ozone-floor-value'}}); @@ -2896,7 +2733,26 @@ describe('ozone Adapter', function () { let testKey2 = 'ozone.singleRequest'; let markbidder_config2 = specMock.getWhitelabelConfigItem(testKey2); expect(markbidder_config2).to.equal('markbidder-singlerequest-value'); - config.resetConfig(); + }); + }); + describe('setBidMediaTypeIfNotExist', function() { + it('should leave the bid object alone if it already contains mediaType', function() { + let thisBid = {mediaType: 'marktest'}; + spec.setBidMediaTypeIfNotExist(thisBid, 'replacement'); + expect(thisBid.mediaType).to.equal('marktest'); + }); + it('should change the bid object if it doesnt already contain mediaType', function() { + let thisBid = {someKey: 'someValue'}; + spec.setBidMediaTypeIfNotExist(thisBid, 'replacement'); + expect(thisBid.mediaType).to.equal('replacement'); + }); + }); + describe('getLoggableBidObject', function() { + it('should return an object without a "renderer" element', function () { + let obj = {'renderer': {}, 'somevalue': '', 'h': 100}; + let ret = spec.getLoggableBidObject(obj); + expect(ret).to.not.have.own.property('renderer'); + expect(ret.h).to.equal(100); }); }); }); From 969f0502d6a53d7af56ec1ed02a9ed40216e94be Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Tue, 9 Aug 2022 19:45:26 +0300 Subject: [PATCH 118/569] Adkernel Bid Adapter: DisplayIo alias (#8803) Adaptor alias for DisplayIo network --- modules/adkernelBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 78df319c5ee..78f00784462 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -94,7 +94,8 @@ export const spec = { {code: 'turktelekom'}, {code: 'felixads'}, {code: 'motionspots'}, - {code: 'sonic_twist'} + {code: 'sonic_twist'}, + {code: 'displayioads'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], From 01fe272dd20d0f58b645f74278be6795a579110a Mon Sep 17 00:00:00 2001 From: Saar Amrani <89377180+saar120@users.noreply.github.com> Date: Tue, 9 Aug 2022 20:16:13 +0300 Subject: [PATCH 119/569] Vidazoo Bid Adapter: pass referrer to server + pubProvidedId support (#8797) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * feat(module): pass referrer to server. * feat(module): added pubProvidedId to supported ID systems. Co-authored-by: Udi Talias Co-authored-by: roman --- modules/vidazooBidAdapter.js | 2 ++ test/spec/modules/vidazooBidAdapter_spec.js | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 7e2000e455a..1f84c9f4b16 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -22,6 +22,7 @@ export const SUPPORTED_ID_SYSTEMS = { 'parrableId': 1, 'pubcid': 1, 'tdid': 1, + 'pubProvidedId': 1 }; const storage = getStorageManager({ gvlid: GVLID, bidderCode: BIDDER_CODE }); @@ -72,6 +73,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { cb: Date.now(), bidFloor: bidFloor, bidId: bidId, + referrer: bidderRequest.refererInfo.ref, adUnitCode: adUnitCode, publisherId: pId, sessionId: sId, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index dc178a9c485..1b4ed4b170d 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -48,7 +48,8 @@ const BIDDER_REQUEST = { }, 'uspConsent': 'consent_string', 'refererInfo': { - 'page': 'https://www.greatsite.com' + 'page': 'https://www.greatsite.com', + 'ref': 'https://www.somereferrer.com' } }; @@ -168,6 +169,7 @@ describe('VidazooBidAdapter', function () { usPrivacy: 'consent_string', sizes: ['300x250', '300x600'], url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', cb: 1000, bidFloor: 0.1, bidId: '2d52001cabd527', From ac5f36d648bc5a2e3e1d500c979c648585d28fed Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 9 Aug 2022 13:39:28 -0400 Subject: [PATCH 120/569] Update aniviewBidAdapter.js (#8802) --- modules/aniviewBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index e97a2531def..00280379133 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -203,7 +203,7 @@ function interpretResponse(serverResponse, bidRequest) { let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); if (xml && xml.getElementsByTagName('parsererror').length == 0) { let cpmData = getCpmData(xml); - if (cpmData && cpmData.cpm > 0) { + if (cpmData.cpm > 0) { bidResponse.requestId = bidRequest.data.bidId; bidResponse.ad = ''; bidResponse.cpm = cpmData.cpm; From 1cae444020a6535e1396fea9f0f71c6a943c172d Mon Sep 17 00:00:00 2001 From: abermanov-zeta <95416296+abermanov-zeta@users.noreply.github.com> Date: Tue, 9 Aug 2022 20:14:12 +0200 Subject: [PATCH 121/569] Zeta Global BidAdapter: update the bid endpoint. (#8801) * Zeta Global BidAdapter: update the bid endpoint. * Zeta Global BidAdapter: fix tests. --- modules/zeta_global_sspBidAdapter.js | 2 +- test/spec/modules/zeta_global_sspBidAdapter_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 86b28021bab..d80265b28bb 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -5,7 +5,7 @@ import {config} from '../src/config.js'; import {parseDomain} from '../src/refererDetection.js'; const BIDDER_CODE = 'zeta_global_ssp'; -const ENDPOINT_URL = 'https://ssp.disqus.com/bid'; +const ENDPOINT_URL = 'https://ssp.disqus.com/bid/prebid'; const USER_SYNC_URL_IFRAME = 'https://ssp.disqus.com/sync?type=iframe'; const USER_SYNC_URL_IMAGE = 'https://ssp.disqus.com/sync?type=image'; const DEFAULT_CUR = 'USD'; diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index e67629cb4f7..a70c1c79828 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -271,7 +271,7 @@ describe('Zeta Ssp Bid Adapter', function () { it('Test required params in banner request', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); const payload = JSON.parse(request.data); - expect(request.url).to.eql('https://ssp.disqus.com/bid?shortname=test_shortname'); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); expect(payload.ext.sid).to.eql('publisherId'); expect(payload.ext.tags.someTag).to.eql(444); expect(payload.ext.tags.shortname).to.be.undefined; @@ -280,7 +280,7 @@ describe('Zeta Ssp Bid Adapter', function () { it('Test required params in video request', function () { const request = spec.buildRequests(videoRequest, videoRequest[0]); const payload = JSON.parse(request.data); - expect(request.url).to.eql('https://ssp.disqus.com/bid?shortname=test_shortname'); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); expect(payload.ext.sid).to.eql('publisherId'); expect(payload.ext.tags.someTag).to.eql(444); expect(payload.ext.tags.shortname).to.be.undefined; From 34d44793e124ae76efcf4840a79ce5f2a778e64d Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Tue, 9 Aug 2022 20:21:24 +0200 Subject: [PATCH 122/569] Adnuntius Bid Adapter: allow Adnuntius to read dimensions (#8806) * package lock fix. * Add dimensions to prebid. --- modules/adnuntiusBidAdapter.js | 4 +++- test/spec/modules/adnuntiusBidAdapter_spec.js | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index b9499af7d9d..0ae8411c073 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -89,7 +89,9 @@ export const spec = { networks[network].adUnits = networks[network].adUnits || []; if (bidderRequest && bidderRequest.refererInfo) networks[network].context = bidderRequest.refererInfo.page; if (adnMeta) networks[network].metaData = adnMeta; - networks[network].adUnits.push({ ...targeting, auId: bid.params.auId, targetId: bid.bidId }); + const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.bidId } + if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes + networks[network].adUnits.push(adUnit); } const networkKeys = Object.keys(networks) diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 403ba63d7d1..2d5ea630f0f 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -13,7 +13,7 @@ describe('adnuntiusBidAdapter', function () { const meta = [{ key: 'usi', value: usi }] before(() => { - const storage = getStorageManager({gvlid: GVLID, moduleName: 'adnuntius'}) + const storage = getStorageManager({ gvlid: GVLID, moduleName: 'adnuntius' }) storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) }); @@ -46,7 +46,11 @@ describe('adnuntiusBidAdapter', function () { auId: '8b6bc', network: 'adnuntius', }, - + mediaTypes: { + banner: { + sizes: [[640, 480], [600, 400]], + } + }, } ] @@ -228,7 +232,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('url'); expect(request[0].url).to.equal(ENDPOINT_URL); expect(request[0]).to.have.property('data'); - expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\"}],\"metaData\":{\"usi\":\"' + usi + '\"}}'); + expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\",\"dimensions\":[[640,480],[600,400]]}],\"metaData\":{\"usi\":\"' + usi + '\"}}'); }); it('Test Video requests', function () { From 8de67ab45c6db4790c0adf4509ed98345f9f34e3 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Wed, 10 Aug 2022 07:36:33 -0700 Subject: [PATCH 123/569] Sharethrough Bid Adapter: Fix `bcat` and `badv` from First Party Data (#8808) * [PGE-178206885] Fix bcat and badv via first party data PGE-178206885 * Fix version number --- modules/sharethroughBidAdapter.js | 6 +++--- test/spec/modules/sharethroughBidAdapter_spec.js | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 731fc5c3a4e..cfbd94096bd 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -4,7 +4,7 @@ import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { createEidsArray } from './userId/eids.js'; -const VERSION = '4.1.1'; +const VERSION = '4.2.0'; const BIDDER_CODE = 'sharethrough'; const SUPPLY_ID = 'WYu2BXv1'; @@ -58,8 +58,8 @@ export const sharethroughAdapterSpec = { schain: bidRequests[0].schain, }, }, - bcat: deepAccess(bidderRequest.ortb2Imp, 'bcat') || bidRequests[0].params.bcat || [], - badv: bidRequests[0].params.badv || [], + bcat: deepAccess(bidderRequest.ortb2, 'bcat') || bidRequests[0].params.bcat || [], + badv: deepAccess(bidderRequest.ortb2, 'badv') || bidRequests[0].params.badv || [], test: 0, }; diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 7addd9ee786..bb5e8e69b6e 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -480,6 +480,8 @@ describe('sharethrough adapter spec', function () { }, }, }, + bcat: ['IAB1', 'IAB2-1'], + badv: ['domain1.com', 'domain2.com'], }; it('should include first party data in open rtb request, site section', () => { @@ -493,13 +495,20 @@ describe('sharethrough adapter spec', function () { }); it('should include first party data in open rtb request, user section', () => { - const openRtbReq = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: firstPartyData})[0].data; + const openRtbReq = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2: firstPartyData })[0].data; expect(openRtbReq.user.yob).to.equal(firstPartyData.user.yob); expect(openRtbReq.user.gender).to.equal(firstPartyData.user.gender); expect(openRtbReq.user.ext.data).to.deep.equal(firstPartyData.user.ext.data); expect(openRtbReq.user.ext.eids).not.to.be.undefined; }); + + it('should include first party data in open rtb request, ORTB blocked section', () => { + const openRtbReq = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2: firstPartyData })[0].data; + + expect(openRtbReq.bcat).to.deep.equal(firstPartyData.bcat); + expect(openRtbReq.badv).to.deep.equal(firstPartyData.badv); + }); }); }); From 2227454e6f21915f951b59dd2e39a032516c8273 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Wed, 10 Aug 2022 20:38:56 +0530 Subject: [PATCH 124/569] resolved merge config issue (#8791) Co-authored-by: pm-azhar-mulla --- src/config.js | 12 ++++++------ test/spec/config_spec.js | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/config.js b/src/config.js index 30ab8f35e2c..5e89210dfcb 100644 --- a/src/config.js +++ b/src/config.js @@ -386,7 +386,11 @@ export function newConfig() { option = Object.assign({}, defaults[topic], option); } - topicalConfig[topic] = config[topic] = option; + try { + topicalConfig[topic] = config[topic] = option; + } catch (e) { + logWarn(`Cannot set config for property ${topic} : `, e) + } }); callSubscribers(topicalConfig); @@ -525,11 +529,7 @@ export function newConfig() { return; } - const mergedConfig = Object.keys(obj).reduce((accum, key) => { - const prevConf = _getConfig()[key] || {}; - accum[key] = mergeDeep(prevConf, obj[key]); - return accum; - }, {}); + const mergedConfig = mergeDeep(_getConfig(), obj); setConfig({ ...mergedConfig }); return mergedConfig; diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 294fce2fa9b..d7f6b9de6c0 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -596,6 +596,7 @@ describe('config API', function () { } setConfig({ + bidderTimeout: 2000, ortb2: { user: { data: [userObj1, userObj2] @@ -609,6 +610,7 @@ describe('config API', function () { }); const rtd = { + bidderTimeout: 3000, ortb2: { user: { data: [userObj1] @@ -623,11 +625,13 @@ describe('config API', function () { mergeConfig(rtd); let ortb2Config = getConfig('ortb2'); + let bidderTimeout = getConfig('bidderTimeout'); expect(ortb2Config.user.data).to.deep.include.members([userObj1, userObj2]); expect(ortb2Config.site.content.data).to.deep.include.members([siteObj1]); expect(ortb2Config.user.data).to.have.lengthOf(2); expect(ortb2Config.site.content.data).to.have.lengthOf(1); + expect(bidderTimeout).to.equal(3000); }); it('should not corrupt global configuration with bidder configuration', () => { From b273c5712fe7311a1cf117ffa42ff143f9f43271 Mon Sep 17 00:00:00 2001 From: Wls-demo <67785512+Wls-demo@users.noreply.github.com> Date: Wed, 10 Aug 2022 19:00:12 +0300 Subject: [PATCH 125/569] Boldwin Bid Adapter: added endpointId param (#8798) * new boldwin bid adapter * fix * Restarting ci / circleci * changes * fixed conflicts * added endpointId param * kick off CircleCI tests Co-authored-by: Aiholkin Co-authored-by: Vladislav Isaiko Co-authored-by: Mykhailo Yaremchuk Co-authored-by: Chris Huie --- modules/boldwinBidAdapter.js | 15 ++++++++++++--- modules/boldwinBidAdapter.md | 17 +++++++++++++++++ test/spec/modules/boldwinBidAdapter_spec.js | 3 ++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js index ec580f8c3c2..ec9aeb2b888 100644 --- a/modules/boldwinBidAdapter.js +++ b/modules/boldwinBidAdapter.js @@ -46,7 +46,7 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && bid.params.placementId); + return Boolean(bid.bidId && bid.params && (bid.params.placementId || bid.params.endpointId)); }, buildRequests: (validBidRequests = [], bidderRequest) => { @@ -85,7 +85,7 @@ export const spec = { for (let i = 0; i < len; i++) { let bid = validBidRequests[i]; - const { mediaTypes } = bid; + const { mediaTypes, params } = bid; const placement = {}; let sizes; if (mediaTypes) { @@ -114,9 +114,18 @@ export const spec = { placement.native = mediaTypes[NATIVE]; } } + + const { placementId, endpointId } = params; + if (placementId) { + placement.placementId = placementId; + placement.type = 'publisher'; + } else if (endpointId) { + placement.endpointId = endpointId; + placement.type = 'network'; + } + placements.push({ ...placement, - placementId: bid.params.placementId, bidId: bid.bidId, sizes: sizes || [], wPlayer: sizes ? sizes[0] : 0, diff --git a/modules/boldwinBidAdapter.md b/modules/boldwinBidAdapter.md index 5e2a5b139b3..47f4b4ffe78 100644 --- a/modules/boldwinBidAdapter.md +++ b/modules/boldwinBidAdapter.md @@ -47,5 +47,22 @@ Module that connects to boldwin demand sources } ] } + // Will return static test banner + { + code: 'endpointId_0', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'boldwin', + params: { + endpointId: 'testBanner', + } + } + ] + }, ]; ``` diff --git a/test/spec/modules/boldwinBidAdapter_spec.js b/test/spec/modules/boldwinBidAdapter_spec.js index afb5f935621..5b51183ea6d 100644 --- a/test/spec/modules/boldwinBidAdapter_spec.js +++ b/test/spec/modules/boldwinBidAdapter_spec.js @@ -59,11 +59,12 @@ describe('BoldwinBidAdapter', function () { expect(data.gdpr).to.not.exist; expect(data.ccpa).to.not.exist; let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'adFormat', 'sizes', 'hPlayer', 'wPlayer', 'schain', 'bidFloor'); + expect(placement).to.have.keys('placementId', 'bidId', 'adFormat', 'sizes', 'hPlayer', 'wPlayer', 'schain', 'bidFloor', 'type'); expect(placement.placementId).to.equal('testBanner'); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.adFormat).to.equal(BANNER); expect(placement.schain).to.be.an('object'); + expect(placement.type).to.exist.and.to.equal('publisher'); }); it('Returns valid data for mediatype video', function () { From c64d0bae03e0e9904481f8f7ccc879f2eab3b2cf Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 10 Aug 2022 11:01:14 -0700 Subject: [PATCH 126/569] ShowHeroes Bid Adapter: add new endpoint (#8816) * add ShowHeroes Adapter * ShowHeroes adapter - expanded outstream support * Revert "ShowHeroes adapter - expanded outstream support" This reverts commit bfcdb913b52012b5afbf95a84956b906518a4b51. * ShowHeroes adapter - expanded outstream support * ShowHeroes adapter - fixes (#4222) * ShowHeroes adapter - banner and outstream fixes (#4222) * ShowHeroes adapter - description and outstream changes (#4222) * ShowHeroes adapter - increase test coverage and small fix * ShowHeroes Adapter - naming convention issue * Mixed AdUnits declaration support * ITDEV-4723 PrebidJS adapter support with SupplyChain module object * ITDEV-4723 Fix tests * ITDEV-4723 New entry point * showheroes-bsBidAdapter: Add support for advertiserDomains * showheroes-bsBidAdapter: hotfix for outstream render * showheroes-bsBidAdapter: update renderer url * showheroes-bsBidAdapter: use only the necessary fields from the gdprConsent * ShowHeroes adapter - added a new endpoint * ShowHeroes adapter - unit tests * Update showheroes-bsBidAdapter.md * kick off tests Co-authored-by: Eldengrof Co-authored-by: veranevera Co-authored-by: Elizaveta Voziyanova <44549195+h2p4x8@users.noreply.github.com> --- modules/showheroes-bsBidAdapter.js | 178 +++++++++++++----- modules/showheroes-bsBidAdapter.md | 49 +++++ .../modules/showheroes-bsBidAdapter_spec.js | 175 ++++++++++++++++- 3 files changed, 352 insertions(+), 50 deletions(-) diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index c1987a32c80..98451ebbb2f 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -1,4 +1,11 @@ -import { deepAccess, getBidIdParameter, getWindowTop, logError } from '../src/utils.js'; +import { + deepAccess, + getBidIdParameter, + getWindowTop, + triggerPixel, + logInfo, + logError +} from '../src/utils.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -7,6 +14,7 @@ import { loadExternalScript } from '../src/adloader.js'; const PROD_ENDPOINT = 'https://bs.showheroes.com/api/v1/bid'; const STAGE_ENDPOINT = 'https://bid-service.stage.showheroes.com/api/v1/bid'; +const VIRALIZE_ENDPOINT = 'https://ads.viralize.tv/prebid-sh/'; const PROD_PUBLISHER_TAG = 'https://static.showheroes.com/publishertag.js'; const STAGE_PUBLISHER_TAG = 'https://pubtag.stage.showheroes.com/publishertag.js'; const PROD_VL = 'https://video-library.showheroes.com'; @@ -26,12 +34,13 @@ export const spec = { aliases: ['showheroesBs'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function(bid) { - return !!bid.params.playerId; + return !!bid.params.playerId || !!bid.params.unitId; }, buildRequests: function(validBidRequests, bidderRequest) { let adUnits = []; - const pageURL = validBidRequests[0].params.contentPageUrl || bidderRequest.refererInfo.page; + const pageURL = validBidRequests[0].params.contentPageUrl || bidderRequest.refererInfo.referer; const isStage = !!validBidRequests[0].params.stage; + const isViralize = !!validBidRequests[0].params.unitId; const isOutstream = deepAccess(validBidRequests[0], 'mediaTypes.video.context') === 'outstream'; const isCustomRender = deepAccess(validBidRequests[0], 'params.outstreamOptions.customRender'); const isNodeRender = deepAccess(validBidRequests[0], 'params.outstreamOptions.slot') || deepAccess(validBidRequests[0], 'params.outstreamOptions.iframe'); @@ -40,12 +49,19 @@ export const spec = { const isBanner = !!validBidRequests[0].mediaTypes.banner || (isOutstream && !(isCustomRender || isNativeRender || isNodeRender)); const defaultSchain = validBidRequests[0].schain || {}; + const consentData = bidderRequest.gdprConsent || {}; + const gdprConsent = { + apiVersion: consentData.apiVersion || 2, + gdprApplies: consentData.gdprApplies || 0, + consentString: consentData.consentString || '', + } + validBidRequests.forEach((bid) => { const videoSizes = getVideoSizes(bid); const bannerSizes = getBannerSizes(bid); const vpaidMode = getBidIdParameter('vpaidMode', bid.params); - const makeBids = (type, size) => { + const makeBids = (type, size, isViralize) => { let context = ''; let streamType = 2; @@ -61,53 +77,79 @@ export const spec = { } } - const consentData = bidderRequest.gdprConsent || {}; - - const gdprConsent = { - apiVersion: consentData.apiVersion || 2, - gdprApplies: consentData.gdprApplies || 0, - consentString: consentData.consentString || '', - } - - return { + let rBid = { type: streamType, adUnitCode: bid.adUnitCode, bidId: bid.bidId, - mediaType: type, context: context, - playerId: getBidIdParameter('playerId', bid.params), auctionId: bidderRequest.auctionId, bidderCode: BIDDER_CODE, - gdprConsent: gdprConsent, start: +new Date(), timeout: 3000, - size: { - width: size[0], - height: size[1] - }, params: bid.params, - schain: bid.schain || defaultSchain, + schain: bid.schain || defaultSchain }; + + if (isViralize) { + rBid.unitId = getBidIdParameter('unitId', bid.params); + rBid.sizes = size; + rBid.mediaTypes = { + [type]: {'context': context} + }; + } else { + rBid.playerId = getBidIdParameter('playerId', bid.params); + rBid.mediaType = type; + rBid.size = { + width: size[0], + height: size[1] + }; + rBid.gdprConsent = gdprConsent; + } + + return rBid; }; - videoSizes.forEach((size) => { - adUnits.push(makeBids(VIDEO, size)); - }); + if (isViralize) { + if (videoSizes && videoSizes[0]) { + adUnits.push(makeBids(VIDEO, videoSizes, isViralize)); + } + if (bannerSizes && bannerSizes[0]) { + adUnits.push(makeBids(BANNER, bannerSizes, isViralize)); + } + } else { + videoSizes.forEach((size) => { + adUnits.push(makeBids(VIDEO, size)); + }); - bannerSizes.forEach((size) => { - adUnits.push(makeBids(BANNER, size)); - }); + bannerSizes.forEach((size) => { + adUnits.push(makeBids(BANNER, size)); + }); + } }); - return { - url: isStage ? STAGE_ENDPOINT : PROD_ENDPOINT, - method: 'POST', - options: {contentType: 'application/json', accept: 'application/json'}, - data: { + let endpointUrl; + let data; + + const QA = validBidRequests[0].params.qa || {}; + + if (isViralize) { + endpointUrl = VIRALIZE_ENDPOINT; + data = { + 'bidRequests': adUnits, + 'context': { + 'gdprConsent': gdprConsent, + 'schain': defaultSchain, + 'pageURL': QA.pageURL || encodeURIComponent(pageURL) + } + } + } else { + endpointUrl = isStage ? STAGE_ENDPOINT : PROD_ENDPOINT; + + data = { 'user': [], 'meta': { 'adapterVersion': 2, - 'pageURL': encodeURIComponent(pageURL), + 'pageURL': QA.pageURL || encodeURIComponent(pageURL), 'vastCacheEnabled': (!!config.getConfig('cache') && !isBanner && !outstreamOptions) || false, 'isDesktop': getWindowTop().document.documentElement.clientWidth > 700, 'xmlAndTag': !!(isOutstream && isCustomRender) || false, @@ -116,6 +158,13 @@ export const spec = { 'requests': adUnits, 'debug': validBidRequests[0].params.debug || false, } + } + + return { + url: QA.endpoint || endpointUrl, + method: 'POST', + options: {contentType: 'application/json', accept: 'application/json'}, + data: data }; }, interpretResponse: function(response, request) { @@ -149,33 +198,53 @@ export const spec = { } return syncs; }, + + onBidWon(bid) { + if (bid.callbacks) { + triggerPixel(bid.callbacks.won); + } + logInfo( + `Showheroes adapter won the auction. Bid id: ${bid.bidId || bid.requestId}` + ); + }, }; function createBids(bidRes, reqData) { - if (bidRes && (!Array.isArray(bidRes.bids) || bidRes.bids.length < 1)) { + if (!bidRes) { + return []; + } + const responseBids = bidRes.bids || bidRes.bidResponses; + if (!Array.isArray(responseBids) || responseBids.length < 1) { return []; } const bids = []; const bidMap = {}; - (reqData.requests || []).forEach((bid) => { + (reqData.requests || reqData.bidRequests || []).forEach((bid) => { bidMap[bid.bidId] = bid; }); - bidRes.bids.forEach(function (bid) { - const reqBid = bidMap[bid.bidId]; + responseBids.forEach(function (bid) { + const requestId = bid.bidId || bid.requestId; + const reqBid = bidMap[requestId]; const currentBidParams = reqBid.params; + const isViralize = !!reqBid.params.unitId; + const size = { + width: bid.width || bid.size.width, + height: bid.height || bid.size.height + }; + let bidUnit = {}; bidUnit.cpm = bid.cpm; - bidUnit.requestId = bid.bidId; + bidUnit.requestId = requestId; bidUnit.adUnitCode = reqBid.adUnitCode; bidUnit.currency = bid.currency; bidUnit.mediaType = bid.mediaType || VIDEO; bidUnit.ttl = TTL; - bidUnit.creativeId = 'c_' + bid.bidId; + bidUnit.creativeId = 'c_' + requestId; bidUnit.netRevenue = true; - bidUnit.width = bid.size.width; - bidUnit.height = bid.size.height; + bidUnit.width = size.width; + bidUnit.height = size.height; bidUnit.meta = { advertiserDomains: bid.adomain || [] }; @@ -185,24 +254,26 @@ function createBids(bidRes, reqData) { content: bid.vastXml, }; } - if (bid.vastTag) { - bidUnit.vastUrl = bid.vastTag; + if (bid.vastTag || bid.vastUrl) { + bidUnit.vastUrl = bid.vastTag || bid.vastUrl; } if (bid.mediaType === BANNER) { bidUnit.ad = getBannerHtml(bid, reqBid, reqData); } else if (bid.context === 'outstream') { const renderer = Renderer.install({ - id: bid.bidId, + id: requestId, url: 'https://static.showheroes.com/renderer.js', adUnitCode: reqBid.adUnitCode, config: { playerId: reqBid.playerId, - width: bid.size.width, - height: bid.size.height, + width: size.width, + height: size.height, vastUrl: bid.vastTag, vastXml: bid.vastXml, + ad: bid.ad, debug: reqData.debug, - isStage: !!reqData.meta.stage, + isStage: reqData.meta && !!reqData.meta.stage, + isViralize: isViralize, customRender: getBidIdParameter('customRender', currentBidParams.outstreamOptions), slot: getBidIdParameter('slot', currentBidParams.outstreamOptions), iframe: getBidIdParameter('iframe', currentBidParams.outstreamOptions), @@ -218,7 +289,12 @@ function createBids(bidRes, reqData) { } function outstreamRender(bid) { - const embedCode = createOutstreamEmbedCode(bid); + let embedCode; + if (bid.renderer.config.isViralize) { + embedCode = createOutstreamEmbedCodeV2(bid); + } else { + embedCode = createOutstreamEmbedCode(bid); + } if (typeof bid.renderer.config.customRender === 'function') { bid.renderer.config.customRender(bid, embedCode); } else { @@ -266,6 +342,12 @@ function createOutstreamEmbedCode(bid) { return fragment; } +function createOutstreamEmbedCodeV2(bid) { + const range = document.createRange(); + range.selectNode(document.getElementsByTagName('body')[0]); + return range.createContextualFragment(getBidIdParameter('ad', bid.renderer.config)); +} + function getBannerHtml (bid, reqBid, reqData) { const isStage = !!reqData.meta.stage; const urls = getEnvURLs(isStage); diff --git a/modules/showheroes-bsBidAdapter.md b/modules/showheroes-bsBidAdapter.md index cde652e9d83..a32a77a2525 100644 --- a/modules/showheroes-bsBidAdapter.md +++ b/modules/showheroes-bsBidAdapter.md @@ -125,3 +125,52 @@ Module that connects to ShowHeroes demand source to fetch bids. } ]; ``` + +# Test Parameters (V2) +``` + var adUnits = [ + { + code: 'video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + } + }, + bids: [ + { + bidder: "showheroes-bs", + params: { + unitId: 'AACBWAcof-611K4U', + vpaidMode: true // by default is 'false' + } + } + ] + }, + { + code: 'video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + } + }, + bids: [ + { + bidder: "showheroes-bs", + params: { + unitId: 'AACBTwsZVANd9NlB', + + outstreamOptions: { + // Required for the outstream renderer to exact node, one of + iframe: 'iframe_id', + // or + slot: 'slot_id' + } + } + } + ] + } + ]; +``` + diff --git a/test/spec/modules/showheroes-bsBidAdapter_spec.js b/test/spec/modules/showheroes-bsBidAdapter_spec.js index 69e8343dfc9..1aa7b132221 100644 --- a/test/spec/modules/showheroes-bsBidAdapter_spec.js +++ b/test/spec/modules/showheroes-bsBidAdapter_spec.js @@ -48,6 +48,18 @@ const bidRequestCommonParams = { 'auctionId': '43aa080090a47f', } +const bidRequestCommonParamsV2 = { + 'bidder': 'showheroes-bs', + 'params': { + 'unitId': 'AACBWAcof-611K4U', + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[640, 480]], + 'bidId': '38b373e1e31c18', + 'bidderRequestId': '12e3ade2543ba6', + 'auctionId': '43aa080090a47f', +} + const bidRequestVideo = { ...bidRequestCommonParams, ...{ @@ -72,6 +84,30 @@ const bidRequestOutstream = { } } +const bidRequestVideoV2 = { + ...bidRequestCommonParamsV2, + ...{ + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream', + } + } + } +} + +const bidRequestOutstreamV2 = { + ...bidRequestCommonParamsV2, + ...{ + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'outstream' + } + } + } +} + const bidRequestVideoVpaid = { ...bidRequestCommonParams, ...{ @@ -129,12 +165,19 @@ describe('shBidAdapter', function () { describe('isBidRequestValid', function () { it('should return true when required params found', function () { - const request = { + const requestV1 = { 'params': { 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', } } - expect(spec.isBidRequestValid(request)).to.equal(true) + expect(spec.isBidRequestValid(requestV1)).to.equal(true) + + const requestV2 = { + 'params': { + 'unitId': 'AACBTwsZVANd9NlB', + } + } + expect(spec.isBidRequestValid(requestV2)).to.equal(true) }) it('should return false when required params are not passed', function () { @@ -149,6 +192,9 @@ describe('shBidAdapter', function () { it('sends bid request to ENDPOINT via POST', function () { const request = spec.buildRequests([bidRequestVideo], bidderRequest) expect(request.method).to.equal('POST') + + const requestV2 = spec.buildRequests([bidRequestVideoV2], bidderRequest) + expect(requestV2.method).to.equal('POST') }) it('check sizes formats', function () { @@ -268,6 +314,30 @@ describe('shBidAdapter', function () { expect(payload2).to.have.property('type', 5); }) + it('should attach valid params to the payload when type is video (instream V2)', function () { + const request = spec.buildRequests([bidRequestVideoV2], bidderRequest) + const payload = request.data.bidRequests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('unitId', 'AACBWAcof-611K4U'); + expect(payload.mediaTypes).to.eql({ + [VIDEO]: { + 'context': 'instream' + } + }); + }) + + it('should attach valid params to the payload when type is video (outstream V2)', function () { + const request = spec.buildRequests([bidRequestOutstreamV2], bidderRequest) + const payload = request.data.bidRequests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('unitId', 'AACBWAcof-611K4U'); + expect(payload.mediaTypes).to.eql({ + [VIDEO]: { + 'context': 'outstream' + } + }); + }) + it('passes gdpr if present', function () { const request = spec.buildRequests([bidRequestVideo], {...bidderRequest, ...gdpr}) const payload = request.data.requests[0]; @@ -275,6 +345,13 @@ describe('shBidAdapter', function () { expect(payload.gdprConsent).to.eql(gdpr.gdprConsent) }) + it('passes gdpr if present (V2)', function () { + const request = spec.buildRequests([bidRequestVideoV2], {...bidderRequest, ...gdpr}) + const context = request.data.context; + expect(context).to.be.an('object'); + expect(context.gdprConsent).to.eql(gdpr.gdprConsent) + }) + it('passes schain object if present', function() { const request = spec.buildRequests([{ ...bidRequestVideo, @@ -284,6 +361,16 @@ describe('shBidAdapter', function () { expect(payload).to.be.an('object'); expect(payload.schain).to.eql(schain.schain); }) + + it('passes schain object if present (V2)', function() { + const request = spec.buildRequests([{ + ...bidRequestVideoV2, + ...schain + }], bidderRequest) + const context = request.data.context; + expect(context).to.be.an('object'); + expect(context.schain).to.eql(schain.schain); + }) }) describe('interpretResponse', function () { @@ -327,6 +414,39 @@ describe('shBidAdapter', function () { }], }; + const basicResponseV2 = { + 'requestId': '38b373e1e31c18', + 'adUnitCode': 'adunit-code-1', + 'cpm': 1, + 'currency': 'EUR', + 'width': 640, + 'height': 480, + 'advertiserDomain': [], + 'callbacks': { + 'won': ['https://api-n729.qa.viralize.com/track/?ver=15&session_id=01ecd03ce381505ccdeb88e555b05001&category=request_session&type=event&request_session_id=01ecd03ce381505ccdeb88e555b05001&label=prebid_won&reason=ok'] + }, + 'mediaType': 'video', + 'adomain': adomain, + }; + + const vastUrl = 'https://api-n729.qa.viralize.com/vast/?zid=AACBWAcof-611K4U&u=https://example.org/?foo=bar&gdpr=0&cs=XXXXXXXXXXXXXXXXXXXX&sid=01ecd03ce381505ccdeb88e555b05001&width=300&height=200&prebidmode=1' + + const responseVideoV2 = { + 'bidResponses': [{ + ...basicResponseV2, + 'context': 'instream', + 'vastUrl': vastUrl, + }], + }; + + const responseVideoOutstreamV2 = { + 'bidResponses': [{ + ...basicResponseV2, + 'context': 'outstream', + 'ad': '', + }], + }; + it('should get correct bid response when type is video', function () { const request = spec.buildRequests([bidRequestVideo], bidderRequest) const expectedResponse = [ @@ -356,6 +476,31 @@ describe('shBidAdapter', function () { expect(result).to.deep.equal(expectedResponse) }) + it('should get correct bid response when type is video (V2)', function () { + const request = spec.buildRequests([bidRequestVideoV2], bidderRequest) + const expectedResponse = [ + { + 'cpm': 1, + 'creativeId': 'c_38b373e1e31c18', + 'adUnitCode': 'adunit-code-1', + 'currency': 'EUR', + 'width': 640, + 'height': 480, + 'mediaType': 'video', + 'netRevenue': true, + 'vastUrl': vastUrl, + 'requestId': '38b373e1e31c18', + 'ttl': 300, + 'meta': { + 'advertiserDomains': adomain + } + } + ] + + const result = spec.interpretResponse({'body': responseVideoV2}, request) + expect(result).to.deep.equal(expectedResponse) + }) + it('should get correct bid response when type is banner', function () { const request = spec.buildRequests([bidRequestBanner], bidderRequest) @@ -396,6 +541,32 @@ describe('shBidAdapter', function () { expect(spots.length).to.equal(1) }) + it('should get correct bid response when type is outstream (slot V2)', function () { + const bidRequestV2 = JSON.parse(JSON.stringify(bidRequestOutstreamV2)); + const slotId = 'testSlot2' + bidRequestV2.params.outstreamOptions = { + slot: slotId + } + + const container = document.createElement('div') + container.setAttribute('id', slotId) + document.body.appendChild(container) + + const request = spec.buildRequests([bidRequestV2], bidderRequest) + + const result = spec.interpretResponse({'body': responseVideoOutstreamV2}, request) + const bid = result[0] + expect(bid).to.have.property('mediaType', VIDEO); + + const renderer = bid.renderer + expect(renderer).to.be.an('object') + expect(renderer.id).to.equal(bidRequestV2.bidId) + renderer.render(bid) + + const scripts = container.querySelectorAll('#testScript') + expect(scripts.length).to.equal(1) + }) + it('should get correct bid response when type is outstream (iframe)', function () { const bidRequest = JSON.parse(JSON.stringify(bidRequestOutstream)); const slotId = 'testIframe' From 9afa2b04fff85cbc4d5a22daf3b506be2a86884c Mon Sep 17 00:00:00 2001 From: Samuel Adu Date: Wed, 10 Aug 2022 20:05:04 +0100 Subject: [PATCH 127/569] Yahoossp bid adapter: Fix schain data handling (#8817) * Yahoossp bid adapter: Fix schain data handling * Add missing trailing semicolon Co-authored-by: slimkrazy --- modules/yahoosspBidAdapter.js | 3 ++- test/spec/modules/yahoosspBidAdapter_spec.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index 926c3317f1d..d1b7b08c202 100644 --- a/modules/yahoosspBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -281,7 +281,8 @@ function generateOpenRtbObject(bidderRequest, bid) { outBoundBidRequest = appendFirstPartyData(outBoundBidRequest, bid); }; - if (deepAccess(bid, 'schain')) { + const schainData = deepAccess(bid, 'schain.nodes'); + if (isArray(schainData) && schainData.length > 0) { outBoundBidRequest.source.ext.schain = bid.schain; outBoundBidRequest.source.ext.schain.nodes[0].rid = outBoundBidRequest.id; }; diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js index 4dce1358fae..6e52e9c57db 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -334,6 +334,19 @@ describe('YahooSSP Bid Adapter:', () => { }); describe('Schain module support:', () => { + it('should not include schain data when schain array is empty', function () { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const globalSchain = { + ver: '1.0', + complete: 1, + nodes: [] + }; + bidRequest.schain = globalSchain; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const schain = data.source.ext.schain; + expect(schain).to.be.undefined; + }); + it('should send Global or Bidder specific schain', function () { const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); const globalSchain = { From 2ee4d3f21f5767734222d0298d3b538e6cc3c859 Mon Sep 17 00:00:00 2001 From: caseywhitmire <60086994+caseywhitmire@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:43:13 -0700 Subject: [PATCH 128/569] Various files: fix LGTM trailing semi-colon (#8811) * akamaiDapRtdProvider: fix LGTM trailing semi-colon * newspassidBidAdapter: fix LGTM trailing semi-colon * native: fix LGTM trailing semi-colon * kueezBidAdapter: fix LGTM trailing semi-colon * shinezBidAdapter: fix LGTM trailing semi-colon * openxOrtbBidAdapter: fix LGTM trailing semi-colon * bliinkBidAdapter: fix LGTM trailing semi-colon * c1xBidAdapter: fix LGTM trailing semi-colon * improvedigitalBidAdapter: fix LGTM trailing semi-colon * weboramaRtdProvider: fix LGTM trailing semi-colon * 1plusXRtdProvider: fix LGTM trailing semi-colon * byDataAnalyticsAdapter: fix LGTM trailing semi-colon * index: fix LGTM trailing semi-colon * userId/index: fix LGTM trailing semi-colon * taboolaBidAdapter: fix LGTM trailing semi-colon * mediafuseBidAdapter: fix LGTM trailing semi-colon * videoheroesBidAdapter: fix LGTM trailing semi-colon * gulpfile: fix LGTM trailing semi-colon * criteoBidAdapter: fix LGTM trailing semi-colon * eplanningBidAdapter: fix LGTM trailing semi-colon * hadronAnalyticsAdapter: fix LGTM trailing semi-colon * config: fix LGTM trailing semi-colon * imuIdSystem: fix LGTM trailing semi-colon * spotxBidAdapter: fix LGTM trailing semi-colon --- gulpfile.js | 10 +++++----- modules/1plusXRtdProvider.js | 2 +- modules/akamaiDapRtdProvider.js | 4 ++-- modules/bliinkBidAdapter.js | 2 +- modules/byDataAnalyticsAdapter.js | 2 +- modules/c1xBidAdapter.js | 2 +- modules/criteoBidAdapter.js | 2 +- modules/eplanningBidAdapter.js | 6 +++--- modules/hadronAnalyticsAdapter.js | 4 ++-- modules/improvedigitalBidAdapter.js | 4 ++-- modules/imuIdSystem.js | 2 +- modules/kueezBidAdapter.js | 4 ++-- modules/mediafuseBidAdapter.js | 16 ++++++++-------- modules/newspassidBidAdapter.js | 2 +- modules/openxOrtbBidAdapter.js | 6 +++--- modules/prebidServerBidAdapter/index.js | 14 +++++++------- modules/shinezBidAdapter.js | 4 ++-- modules/spotxBidAdapter.js | 10 +++++----- modules/taboolaBidAdapter.js | 2 +- modules/userId/index.js | 2 +- modules/videoheroesBidAdapter.js | 2 +- modules/weboramaRtdProvider.js | 6 +++--- src/config.js | 2 +- src/native.js | 2 +- 24 files changed, 56 insertions(+), 56 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 3425850286d..09de874e389 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -246,7 +246,7 @@ function bundle(dev, moduleArr) { const dependencies = new Set(); [coreFile].concat(moduleFiles).map(name => path.basename(name)).forEach((file) => { (depGraph[file] || []).forEach((dep) => dependencies.add(helpers.getBuiltPath(dev, dep))); - }) + }); const entries = [coreFile].concat(Array.from(dependencies), moduleFiles); @@ -449,7 +449,7 @@ gulp.task('build-bundle-verbose', gulp.series(makeWebpackPkg({ // public tasks (dependencies are needed for each task since they can be ran on their own) gulp.task('test-only', test); -gulp.task('test-all-features-disabled', testTaskMaker({disableFeatures: require('./features.json'), oneBrowser: 'chrome', watch: false})) +gulp.task('test-all-features-disabled', testTaskMaker({disableFeatures: require('./features.json'), oneBrowser: 'chrome', watch: false})); gulp.task('test', gulp.series(clean, lint, gulp.series('test-all-features-disabled', 'test-only'))); gulp.task('test-coverage', gulp.series(clean, testCoverage)); @@ -464,12 +464,12 @@ gulp.task('serve', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', wa gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast))); gulp.task('serve-prod', gulp.series(clean, gulp.parallel('build-bundle-prod', startLocalServer))); gulp.task('serve-and-test', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); -gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))) -gulp.task('serve-e2e-dev', gulp.series(clean, 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))) +gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); +gulp.task('serve-e2e-dev', gulp.series(clean, 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))); gulp.task('default', gulp.series(clean, 'build-bundle-prod')); -gulp.task('e2e-test-only', () => runWebdriver({file: argv.file})) +gulp.task('e2e-test-only', () => runWebdriver({file: argv.file})); gulp.task('e2e-test', gulp.series(clean, 'build-bundle-prod', testTaskMaker({e2e: true}))); // other tasks gulp.task(bundleToStdout); diff --git a/modules/1plusXRtdProvider.js b/modules/1plusXRtdProvider.js index 5affafcf9d3..9d010c8d298 100644 --- a/modules/1plusXRtdProvider.js +++ b/modules/1plusXRtdProvider.js @@ -138,7 +138,7 @@ export const updateBidderConfig = (bidder, ortb2Updates, bidderConfigs) => { if (site) { // Legacy : cf. comment on buildOrtb2Updates first lines - const currentSite = deepAccess(bidderConfigCopy, 'ortb2.site') + const currentSite = deepAccess(bidderConfigCopy, 'ortb2.site'); const updatedSite = mergeDeep(currentSite, site); deepSetValue(bidderConfigCopy, 'ortb2.site', updatedSite); } diff --git a/modules/akamaiDapRtdProvider.js b/modules/akamaiDapRtdProvider.js index f167cb7f3ea..1c2af70d737 100644 --- a/modules/akamaiDapRtdProvider.js +++ b/modules/akamaiDapRtdProvider.js @@ -202,7 +202,7 @@ export const dapUtils = { dapUtils.dapTokenize(configAsync, config.identity, onDone, function(token, status, xhr, onDone) { item.expires_at = now + DAP_DEFAULT_TOKEN_TTL; - let exp = dapUtils.dapExtractExpiryFromToken(token) + let exp = dapUtils.dapExtractExpiryFromToken(token); if (typeof exp == 'number') { item.expires_at = exp - 10; } @@ -303,7 +303,7 @@ export const dapUtils = { dapUtils.dapEncryptedMembership(configAsync, token, onDone, function(encToken, status, xhr, onDone) { item.expires_at = now + DAP_DEFAULT_TOKEN_TTL; - let exp = dapUtils.dapExtractExpiryFromToken(encToken) + let exp = dapUtils.dapExtractExpiryFromToken(encToken); if (typeof exp == 'number') { item.expires_at = exp - 10; } diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js index 495862013de..127b534c989 100644 --- a/modules/bliinkBidAdapter.js +++ b/modules/bliinkBidAdapter.js @@ -96,7 +96,7 @@ export function getKeywords() { * @return {({cpm, netRevenue: boolean, requestId, width: number, currency, ttl: number, creativeId, height: number}&{mediaType: string, vastXml})|null} */ export const buildBid = (bidResponse) => { - const mediaType = deepAccess(bidResponse, 'creative.media_type') + const mediaType = deepAccess(bidResponse, 'creative.media_type'); if (!mediaType) return null; let bid; diff --git a/modules/byDataAnalyticsAdapter.js b/modules/byDataAnalyticsAdapter.js index ccf57942d7a..84479ee618b 100644 --- a/modules/byDataAnalyticsAdapter.js +++ b/modules/byDataAnalyticsAdapter.js @@ -266,7 +266,7 @@ ascAdapter.getVisitorData = function (data = {}) { } const { clientId } = initOptions; - var userId = storage.getDataFromLocalStorage('userId') + var userId = storage.getDataFromLocalStorage('userId'); if (!userId) { userId = generateUid(); storage.setDataInLocalStorage('userId', userId); diff --git a/modules/c1xBidAdapter.js b/modules/c1xBidAdapter.js index 8b579f52299..8c9407825ba 100644 --- a/modules/c1xBidAdapter.js +++ b/modules/c1xBidAdapter.js @@ -78,7 +78,7 @@ export const c1xAdapter = { url: URL, data: payloadString, bids: bidIdTags - }) + }); return bidRequest; }, diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 590062c76bd..82035d1550e 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -513,7 +513,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { ext: { schain: schain } - } + }; }; request.user = { ext: bidderRequest.userExt diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index a6adab1e9b9..0d37b72f4ad 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -38,14 +38,14 @@ export const spec = { const spaces = getSpaces(bidRequests, urlConfig.ml); // TODO: do the fallbacks make sense here? const pageUrl = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; - const domain = bidderRequest.refererInfo.domain || window.location.host + const domain = bidderRequest.refererInfo.domain || window.location.host; if (urlConfig.t) { url = 'https://' + urlConfig.isv + '/layers/t_pbjs_2.json'; params = {}; } else { url = 'https://' + (urlConfig.sv || DEFAULT_SV) + '/pbjs/1/' + urlConfig.ci + '/' + dfpClientId + '/' + domain + '/' + sec; // TODO: does the fallback make sense here? - const referrerUrl = bidderRequest.refererInfo.ref || bidderRequest.refererInfo.topmostLocation + const referrerUrl = bidderRequest.refererInfo.ref || bidderRequest.refererInfo.topmostLocation; if (storage.hasLocalStorage()) { registerViewabilityAllBids(bidRequests); @@ -150,7 +150,7 @@ export const spec = { return syncs; }, -} +}; function getUserAgent() { return window.navigator.userAgent; diff --git a/modules/hadronAnalyticsAdapter.js b/modules/hadronAnalyticsAdapter.js index 073dd15ea49..52829cf754d 100644 --- a/modules/hadronAnalyticsAdapter.js +++ b/modules/hadronAnalyticsAdapter.js @@ -10,7 +10,7 @@ import {getRefererInfo} from '../src/refererDetection.js'; * hadronAnalyticsAdapter.js - Audigent Hadron Analytics Adapter */ -const HADRON_ANALYTICS_URL = 'https://analytics.hadron.ad.gt/api/v1/analytics' +const HADRON_ANALYTICS_URL = 'https://analytics.hadron.ad.gt/api/v1/analytics'; const HADRONID_ANALYTICS_VER = 'pbadgt0'; const DEFAULT_PARTNER_ID = 0; const AU_GVLID = 561; @@ -152,7 +152,7 @@ hadronAnalyticsAdapter.enableAnalytics = function(conf = {}) { } hadronAnalyticsAdapter.originEnableAnalytics(conf); -} +}; function flush() { // Don't send anything if no partner id was declared diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index a362f85654c..4996f0efaf0 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -123,7 +123,7 @@ export const spec = { if (bidderRequest) { // GDPR - const gdprConsent = deepAccess(bidderRequest, 'gdprConsent') + const gdprConsent = deepAccess(bidderRequest, 'gdprConsent'); if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { deepSetValue(request, 'regs.ext.gdpr', Number(gdprConsent.gdprApplies)); @@ -508,7 +508,7 @@ const ID_REQUEST = { const url = deepAccess(bidderRequest, 'refererInfo.page'); if (url) { site.page = url; - site.domain = bidderRequest.refererInfo.domain + site.domain = bidderRequest.refererInfo.domain; } const configSiteSettings = config.getConfig('site') || {}; const fpdSiteSettings = deepAccess(bidderRequest, 'ortb2.site') || {}; diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js index 24b11dc1667..41ff95b6702 100644 --- a/modules/imuIdSystem.js +++ b/modules/imuIdSystem.js @@ -146,7 +146,7 @@ export const imuIdSubmodule = { } if (!localData.id) { - return {callback: callImuidApi(apiUrl)} + return {callback: callImuidApi(apiUrl)}; } if (localData.expired) { callImuidApi(apiUrl)(); diff --git a/modules/kueezBidAdapter.js b/modules/kueezBidAdapter.js index 4e0278603fd..24d339d1938 100644 --- a/modules/kueezBidAdapter.js +++ b/modules/kueezBidAdapter.js @@ -309,7 +309,7 @@ function generateSharedParams(sharedParams, bidderRequest) { wrapper_type: 'prebidjs', wrapper_vendor: '$$PREBID_GLOBAL$$', wrapper_version: '$prebid.version$' - } + }; if (syncEnabled) { const allowedSyncMethod = getSyncMethod(filterSettings, bidderCode); @@ -352,7 +352,7 @@ function generateSharedParams(sharedParams, bidderRequest) { params.userIds = JSON.stringify(userIds); } - return params + return params; } /** diff --git a/modules/mediafuseBidAdapter.js b/modules/mediafuseBidAdapter.js index 5a96c8b286c..e61c2e65c39 100644 --- a/modules/mediafuseBidAdapter.js +++ b/modules/mediafuseBidAdapter.js @@ -177,7 +177,7 @@ export const spec = { payload['iab_support'] = { omidpn: 'Mediafuse', omidpv: '$prebid.version$' - } + }; } if (member > 0) { @@ -185,7 +185,7 @@ export const spec = { } if (appDeviceObjBid) { - payload.device = appDeviceObj + payload.device = appDeviceObj; } if (appIdObjBid) { payload.app = appIdObj; @@ -227,7 +227,7 @@ export const spec = { } if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent + payload.us_privacy = bidderRequest.uspConsent; } if (bidderRequest && bidderRequest.refererInfo) { @@ -237,7 +237,7 @@ export const spec = { rd_top: bidderRequest.refererInfo.reachedTop, rd_ifs: bidderRequest.refererInfo.numIframes, rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - } + }; payload.referrer_detection = refererinfo; } @@ -386,7 +386,7 @@ export const spec = { reloadViewabilityScriptWithCorrectParameters(bid); } } -} +}; function isPopulatedArray(arr) { return !!(isArray(arr) && arr.length > 0); @@ -404,7 +404,7 @@ function reloadViewabilityScriptWithCorrectParameters(bid) { if (viewJsPayload) { let prebidParams = 'pbjs_adid=' + bid.adId + ';pbjs_auc=' + bid.adUnitCode; - let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload) + let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload); let newJsTrackerSrc = jsTrackerSrc.replace('dom_id=%native_dom_id%', prebidParams); @@ -498,7 +498,7 @@ function formatRequest(payload, bidderRequest) { if (getParameterByName('apn_test').toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { options.customHeaders = { 'X-Is-Test': 1 - } + }; } if (payload.tags.length > MAX_IMPS_PER_REQUEST) { @@ -733,7 +733,7 @@ function bidToTag(bid) { tag.code = bid.params.invCode; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; let bidFloor = getBidFloor(bid); diff --git a/modules/newspassidBidAdapter.js b/modules/newspassidBidAdapter.js index ee6ece2b033..409cb55e6a0 100644 --- a/modules/newspassidBidAdapter.js +++ b/modules/newspassidBidAdapter.js @@ -66,7 +66,7 @@ export const spec = { this.loadConfiguredData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code - let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED' + let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED'; if (!(bid.params.hasOwnProperty('placementId'))) { logError(err1.replace('{param}', 'placementId'), adUnitCode); return false; diff --git a/modules/openxOrtbBidAdapter.js b/modules/openxOrtbBidAdapter.js index bbe0e827b8e..62066ebe4f1 100644 --- a/modules/openxOrtbBidAdapter.js +++ b/modules/openxOrtbBidAdapter.js @@ -157,7 +157,7 @@ function createVideoRequest(bid, bidderRequest) { method: 'POST', url: REQUEST_URL, data: data - } + }; } function getBaseRequest(bid, bidderRequest) { @@ -191,7 +191,7 @@ function getBaseRequest(bid, bidderRequest) { utils.deepSetValue(req, 'ext.delDomain', bid.params.delDomain); } if (bid.params.test) { - req.test = 1 + req.test = 1; } if (bidderRequest.gdprConsent) { if (bidderRequest.gdprConsent.gdprApplies !== undefined) { @@ -309,7 +309,7 @@ function interpretResponse(resp, req) { response.meta.paf.content_id = utils.deepAccess(bid, 'ext.paf.content_id'); } - return response + return response; })]; }); diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 9c7c3c921b0..d99013c61f3 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -360,7 +360,7 @@ function _appendSiteAppDevice(request, pageUrl, accountId) { // ORTB specifies app OR site if (typeof config.getConfig('app') === 'object') { request.app = config.getConfig('app'); - request.app.publisher = {id: accountId} + request.app.publisher = {id: accountId}; } else { request.site = {}; if (isPlainObject(config.getConfig('site'))) { @@ -531,7 +531,7 @@ Object.assign(ORTB2.prototype, { if (bid.mediaTypes != null) { logWarn(`Prebid Server adapter does not (yet) support bidder-specific mediaTypes for the same adUnit. Size mapping configuration will be ignored for adUnit: ${adUnit.code}, bidder: ${bid.bidder}`); } - }) + }); // in case there is a duplicate imp.id, add '-2' suffix to the second imp.id. // e.g. if there are 2 adUnits (case of twin adUnit codes) with code 'test', @@ -599,7 +599,7 @@ Object.assign(ORTB2.prototype, { }, {}); } } - const nativeReq = deepAccess(adUnit, 'nativeOrtbRequest') + const nativeReq = deepAccess(adUnit, 'nativeOrtbRequest'); if (FEATURES.NATIVE && nativeReq) { const defaultRequest = { // TODO: determine best way to pass these and if we allow defaults @@ -727,7 +727,7 @@ Object.assign(ORTB2.prototype, { if (floor) { imp.bidfloor = floor.floor; - imp.bidfloorcur = floor.currency + imp.bidfloorcur = floor.currency; } if (imp.banner || imp.video || imp.native) { @@ -766,11 +766,11 @@ Object.assign(ORTB2.prototype, { } // This is no longer overwritten unless name and version explicitly overwritten by extPrebid (mergeDeep) - request.ext.prebid = Object.assign(request.ext.prebid, {channel: {name: 'pbjs', version: $$PREBID_GLOBAL$$.version}}) + request.ext.prebid = Object.assign(request.ext.prebid, {channel: {name: 'pbjs', version: $$PREBID_GLOBAL$$.version}}); // set debug flag if in debug mode if (getConfig('debug')) { - request.ext.prebid = Object.assign(request.ext.prebid, {debug: true}) + request.ext.prebid = Object.assign(request.ext.prebid, {debug: true}); } // s2sConfig video.ext.prebid is passed through openrtb to PBS @@ -1010,7 +1010,7 @@ Object.assign(ORTB2.prototype, { if (isPlainObject(ortb) && Array.isArray(ortb.assets)) { bidObject.native = { ortb, - } + }; } else { logError('prebid server native response contained no assets'); } diff --git a/modules/shinezBidAdapter.js b/modules/shinezBidAdapter.js index 923ecec7e4b..0ce2eed6479 100644 --- a/modules/shinezBidAdapter.js +++ b/modules/shinezBidAdapter.js @@ -382,7 +382,7 @@ function generateGeneralParams(generalObject, bidderRequest) { ua: navigator.userAgent, session_id: getBidIdParameter('auctionId', generalObject), tmax: timeout - } + }; const userIdsParam = getBidIdParameter('userId', generalObject); if (userIdsParam) { @@ -431,5 +431,5 @@ function generateGeneralParams(generalObject, bidderRequest) { generalParams.page_url = generalParams.page_url || config.getConfig('pageUrl') || deepAccess(window, 'location.href'); } - return generalParams + return generalParams; } diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index d044df74cfe..8a18d4973d3 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -268,7 +268,7 @@ export const spec = { ext: bid.userId.id5id.ext || {} }] } - ) + ); } // Add common id if available @@ -297,14 +297,14 @@ export const spec = { } }] } - ) + ); } // Only add the user object if it's not empty if (!isEmpty(userExt)) { requestPayload.user = { ext: userExt }; } - const urlQueryParams = 'src_sys=prebid' + const urlQueryParams = 'src_sys=prebid'; return { method: 'POST', url: URL + channelId + '?' + urlQueryParams, @@ -365,7 +365,7 @@ export const spec = { bid.vastXml = spotxBid.adm; } else { bid.cache_key = spotxBid.ext.cache_key; - bid.vastUrl = 'https://search.spotxchange.com/ad/vast.html?key=' + spotxBid.ext.cache_key + bid.vastUrl = 'https://search.spotxchange.com/ad/vast.html?key=' + spotxBid.ext.cache_key; bid.videoCacheKey = spotxBid.ext.cache_key; } @@ -453,7 +453,7 @@ function outstreamRender(bid) { loadExternalScript(easiUrl, BIDDER_CODE, undefined, undefined, attributes); } } catch (err) { - logError('[SPOTX][renderer] Error:' + err.message) + logError('[SPOTX][renderer] Error:' + err.message); } } } diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index e94e7aba7ca..8db98409a76 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -107,7 +107,7 @@ export const spec = { } if (config.getConfig('coppa')) { - regs.coppa = 1 + regs.coppa = 1; } const ortb2 = bidderRequest.ortb2 || { diff --git a/modules/userId/index.js b/modules/userId/index.js index f925e637178..14ebe6a57e8 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -843,7 +843,7 @@ function initSubmodules(dest, submodules, consentData, forceRefresh = false) { submodules = submodules.filter((submod) => !submod.config.storage || storageTypes.has(submod.config.storage.type)); if (!submodules.length) { - logWarn(`${MODULE_NAME} - no ID module is configured for one of the available storage types:`, Array.from(storageTypes)) + logWarn(`${MODULE_NAME} - no ID module is configured for one of the available storage types:`, Array.from(storageTypes)); return []; } diff --git a/modules/videoheroesBidAdapter.js b/modules/videoheroesBidAdapter.js index 089f1256937..4818d9e1c58 100644 --- a/modules/videoheroesBidAdapter.js +++ b/modules/videoheroesBidAdapter.js @@ -85,7 +85,7 @@ export const spec = { }; if (bidderRequest.refererInfo.ref) { - data.site.ref = bidderRequest.refererInfo.ref + data.site.ref = bidderRequest.refererInfo.ref; } if (bidderRequest.gdprConsent) { diff --git a/modules/weboramaRtdProvider.js b/modules/weboramaRtdProvider.js index 35440849db4..533e669fcaa 100644 --- a/modules/weboramaRtdProvider.js +++ b/modules/weboramaRtdProvider.js @@ -199,7 +199,7 @@ function initSubSection(moduleParams, subSection, ...requiredFields) { }); } catch (e) { logError(`unable to initialize: error on ${subSection} configuration: ${e}`); - return false + return false; } logMessage(`weborama ${subSection} initialized with success`); @@ -213,7 +213,7 @@ const globalDefaults = { sendToBidders: true, onData: () => { /* do nothing */ }, -} +}; /** normalize submodule configuration * @param {ModuleParams} moduleParams @@ -558,7 +558,7 @@ function getDataFromLocalStorage(weboDataConf, cacheGet, cacheSet, defaultLocalS } } - const profile = cacheGet() + const profile = cacheGet(); if (profile) { return [profile, false]; diff --git a/src/config.js b/src/config.js index 5e89210dfcb..0922a2a2bea 100644 --- a/src/config.js +++ b/src/config.js @@ -456,7 +456,7 @@ export function newConfig() { if (options.init) { if (topic === ALL_TOPICS) { - callback(getConfig()) + callback(getConfig()); } else { // eslint-disable-next-line standard/no-callback-literal callback({[topic]: getConfig(topic)}); diff --git a/src/native.js b/src/native.js index 4ba4505d441..bd20aa5d399 100644 --- a/src/native.js +++ b/src/native.js @@ -351,7 +351,7 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) { value = placeholder; } - let assetSendTargetingKeys = deepAccess(adUnit, `nativeParams.${asset}.sendTargetingKeys`) + let assetSendTargetingKeys = deepAccess(adUnit, `nativeParams.${asset}.sendTargetingKeys`); if (typeof assetSendTargetingKeys !== 'boolean') { assetSendTargetingKeys = deepAccess(adUnit, `nativeParams.ext.${asset}.sendTargetingKeys`); } From 865cb34533696ee5c2109926b86c390e9ef1507b Mon Sep 17 00:00:00 2001 From: Pgb-Criteo <92986445+Pgb-Criteo@users.noreply.github.com> Date: Thu, 11 Aug 2022 02:45:49 +0200 Subject: [PATCH 129/569] Criteo Adapter: Add Price floor support (#8815) --- modules/criteoBidAdapter.js | 41 +++++- test/spec/modules/criteoBidAdapter_spec.js | 145 ++++++++++++++++++++- 2 files changed, 184 insertions(+), 2 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 82035d1550e..c7f843f421d 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -502,6 +502,9 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { slot.video = video; } + + enrichSlotWithFloors(slot, bidRequest) + return slot; }), }; @@ -537,7 +540,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { return request; } -function parseSizes(sizes, parser) { +function parseSizes(sizes, parser = s => s) { if (sizes == undefined) { return []; } @@ -635,6 +638,42 @@ for (var i = 0; i < 10; ++i) { `; } +function enrichSlotWithFloors(slot, bidRequest) { + try { + const slotFloors = {}; + + if (bidRequest.getFloor) { + if (bidRequest.mediaTypes?.banner) { + slotFloors.banner = {}; + const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')) + bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({size: bannerSize, mediaType: BANNER})); + } + + if (bidRequest.mediaTypes?.video) { + slotFloors.video = {}; + const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) + videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({size: videoSize, mediaType: VIDEO})); + } + + if (bidRequest.mediaTypes?.native) { + slotFloors.native = {}; + slotFloors.native['*'] = bidRequest.getFloor({size: '*', mediaType: NATIVE}); + } + + if (Object.keys(slotFloors).length > 0) { + if (!slot.ext) { + slot.ext = {} + } + Object.assign(slot.ext, { + floors: slotFloors + }); + } + } + } catch (e) { + logError('Could not parse floors from Prebid: ' + e); + } +} + export function canFastBid(fastBidVersion) { return fastBidVersion !== FAST_BID_VERSION_NONE; } diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 19e97ea85bb..7252232676f 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -13,7 +13,7 @@ import * as utils from 'src/utils.js'; import * as refererDetection from 'src/refererDetection.js'; import { config } from '../../../src/config.js'; import * as storageManager from 'src/storageManager.js'; -import { NATIVE, VIDEO } from '../../../src/mediaTypes.js'; +import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; describe('The Criteo bidding adapter', function () { let utilsMock, sandbox; @@ -1238,6 +1238,149 @@ describe('The Criteo bidding adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.regs.coppa).to.be.undefined; }); + + it('should properly build a banner request with floors', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + } + }, + params: { + networkId: 456, + }, + + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0}; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0}; + } else { + return {} + } + } + }, + ]; + const bidderRequest = {}; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.slots[0].ext.floors).to.deep.equal({ + 'banner': { + '300x250': {'currency': 'USD', 'floor': 1}, + '728x90': {'currency': 'USD', 'floor': 2} + }}); + }); + + it('should properly build a video request with several player sizes with floors', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + video: { + playerSize: [[300, 250], [728, 90]] + } + }, + params: { + networkId: 456, + }, + + getFloor: inputParams => { + if (inputParams.mediaType === VIDEO && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0}; + } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0}; + } else { + return {} + } + } + }, + ]; + const bidderRequest = {}; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.slots[0].ext.floors).to.deep.equal({ + 'video': { + '300x250': {'currency': 'USD', 'floor': 1}, + '728x90': {'currency': 'USD', 'floor': 2} + }}); + }); + + it('should properly build a multi format request with floors', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + }, + video: { + playerSize: [640, 480], + }, + native: {} + }, + params: { + networkId: 456, + }, + ortb2Imp: { + ext: { + data: { + someContextAttribute: 'abc' + } + } + }, + + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0}; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0}; + } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 640 && inputParams.size[1] === 480) { + return { + currency: 'EUR', + floor: 3.2}; + } else if (inputParams.mediaType === NATIVE && inputParams.size === '*') { + return { + currency: 'YEN', + floor: 4.99}; + } else { + return {} + } + } + }, + ]; + const bidderRequest = {}; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.slots[0].ext.data.someContextAttribute).to.deep.equal('abc'); + expect(request.data.slots[0].ext.floors).to.deep.equal({ + 'banner': { + '300x250': {'currency': 'USD', 'floor': 1}, + '728x90': {'currency': 'USD', 'floor': 2} + }, + 'video': { + '640x480': {'currency': 'EUR', 'floor': 3.2} + }, + 'native': { + '*': {'currency': 'YEN', 'floor': 4.99} + }}); + }); }); describe('interpretResponse', function () { From 5ef54f7b2234cba7595ea4446f68b821af670bd6 Mon Sep 17 00:00:00 2001 From: caseywhitmire <60086994+caseywhitmire@users.noreply.github.com> Date: Thu, 11 Aug 2022 05:11:28 -0700 Subject: [PATCH 130/569] Various files: fix LGTM trailing semi-colon (#8821) * Update ozoneBidAdapter * Update criteoBidAdapter * Update sspBCBidAdapter * Update pubmaticBidAdapter.js * Update ttdBidAdapter.js * Update tripleliftBidAdapter.js * Update colossussspBidAdapter.js * Update sirdataRtdProvider.js * Update smartxBidAdapter.js * Update mobfoxpbBidAdapter.js * Update 33acrossBidAdapter.js * Update gridNMBidAdapter.js * Update consentManagementUsp.js * Update adapterManager.js * Update adapterManager.js * Update apacdexBidAdapter.js * Update microadBidAdapter.js * Update pubmaticAnalyticsAdapter.js * Update hadronRtdProvider.js * Update adagioBidAdapter.js * Update operaadsBidAdapter.js * Update auction.js * Update appnexusBidAdapter.js * Update utils.js * Update adgenerationBidAdapter.js * Update pulsepointBidAdapter.js * Update consumableBidAdapter.js * Update boldwinBidAdapter.js * Update relaidoBidAdapter.js * Update rubiconBidAdapter.js * Update vrtcalBidAdapter.js * Update oguryBidAdapter.js * Update riseBidAdapter.js * Update gridBidAdapter.js * Update merkleIdSystem.js --- modules/33acrossBidAdapter.js | 6 +++--- modules/adagioBidAdapter.js | 2 +- modules/adgenerationBidAdapter.js | 2 +- modules/apacdexBidAdapter.js | 6 +++--- modules/appnexusBidAdapter.js | 18 +++++++++--------- modules/boldwinBidAdapter.js | 8 ++++---- modules/colossussspBidAdapter.js | 4 ++-- modules/consentManagementUsp.js | 2 +- modules/consumableBidAdapter.js | 2 +- modules/criteoBidAdapter.js | 2 +- modules/gridBidAdapter.js | 2 +- modules/gridNMBidAdapter.js | 2 +- modules/hadronRtdProvider.js | 2 +- modules/merkleIdSystem.js | 4 ++-- modules/microadBidAdapter.js | 2 +- modules/mobfoxpbBidAdapter.js | 4 ++-- modules/oguryBidAdapter.js | 2 +- modules/operaadsBidAdapter.js | 2 +- modules/ozoneBidAdapter.js | 4 ++-- modules/pubmaticAnalyticsAdapter.js | 2 +- modules/pubmaticBidAdapter.js | 8 ++++---- modules/pulsepointBidAdapter.js | 2 +- modules/relaidoBidAdapter.js | 4 ++-- modules/riseBidAdapter.js | 4 ++-- modules/rubiconBidAdapter.js | 4 ++-- modules/sirdataRtdProvider.js | 6 +++--- modules/smartxBidAdapter.js | 4 ++-- modules/sspBCBidAdapter.js | 4 ++-- modules/tripleliftBidAdapter.js | 2 +- modules/ttdBidAdapter.js | 8 ++++---- modules/vrtcalBidAdapter.js | 2 +- src/adapterManager.js | 6 +++--- src/auction.js | 4 ++-- src/utils.js | 2 +- 34 files changed, 69 insertions(+), 69 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index ad2b092baa8..89c00897571 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -322,7 +322,7 @@ function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageU 'url': url, 'data': JSON.stringify(ttxRequest), 'options': options - } + }; } // BUILD REQUESTS: SET EXTENSIONS @@ -438,7 +438,7 @@ function _buildBannerORTB(bidRequest) { return { format, ext - } + }; } // BUILD REQUESTS: VIDEO @@ -452,7 +452,7 @@ function _buildVideoORTB(bidRequest) { ...videoBidderParams // Bidder Specific overrides }; - const video = {} + const video = {}; const { w, h } = _getSize(videoParams.playerSize[0]); video.w = w; diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index afc15bb2d17..5c07cbe2844 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1124,7 +1124,7 @@ export const spec = { ...globalFeatures, print_number: getPrintNumber(adagioBid.adUnitCode, adagioBidderRequest).toString(), adunit_position: getSlotPosition(adagioBid.params.adUnitElementId) // adUnitElementId à déplacer ??? - } + }; adagioBid.params.pageviewId = internal.getPageviewId(); adagioBid.params.prebidVersion = '$prebid.version$'; diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index c91c4ad1ae0..21395724609 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -200,7 +200,7 @@ function createNativeAd(body) { native.clickTrackers = body.native_ad.link.clicktrackers || []; native.impressionTrackers = body.native_ad.imptrackers || []; if (body.beaconurl && body.beaconurl != '') { - native.impressionTrackers.push(body.beaconurl) + native.impressionTrackers.push(body.beaconurl); } } return native; diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index a6ab1ea03da..10593855c59 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -103,7 +103,7 @@ export const spec = { var pageUrl = _extractTopWindowUrlFromBidderRequest(bidderRequest); payload.site = {}; - payload.site.page = pageUrl + payload.site.page = pageUrl; payload.site.referrer = _extractTopWindowReferrerFromBidderRequest(bidderRequest); // TODO: does it make sense to fall back to window.location for the domain? payload.site.hostname = bidderRequest.refererInfo?.domain || parseDomain(pageUrl); @@ -124,12 +124,12 @@ export const spec = { // Apply schain. if (schain) { - payload.schain = schain + payload.schain = schain; } // Apply eids. if (eids) { - payload.eids = eids + payload.eids = eids; } // Apply geo diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 056326eb824..cf73b69991f 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -215,7 +215,7 @@ export const spec = { payload['iab_support'] = { omidpn: 'Appnexus', omidpv: '$prebid.version$' - } + }; } if (member > 0) { @@ -223,7 +223,7 @@ export const spec = { } if (appDeviceObjBid) { - payload.device = appDeviceObj + payload.device = appDeviceObj; } if (appIdObjBid) { payload.app = appIdObj; @@ -265,7 +265,7 @@ export const spec = { } if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent + payload.us_privacy = bidderRequest.uspConsent; } if (bidderRequest && bidderRequest.refererInfo) { @@ -275,7 +275,7 @@ export const spec = { rd_top: bidderRequest.refererInfo.reachedTop, rd_ifs: bidderRequest.refererInfo.numIframes, rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - } + }; let pubPageUrl = bidderRequest.refererInfo.canonicalUrl; if (isStr(pubPageUrl) && pubPageUrl !== '') { refererinfo.rd_can = pubPageUrl; @@ -410,7 +410,7 @@ export const spec = { if (includes(s2sCfg.bidders, adUnit.bids[0].bidder)) { s2sEndpointUrl = deepAccess(s2sCfg, 'endpoint.p1Consent'); } - }) + }); } if (s2sEndpointUrl && s2sEndpointUrl.match('/openrtb2/prebid')) { @@ -455,7 +455,7 @@ export const spec = { reloadViewabilityScriptWithCorrectParameters(bid); } } -} +}; function isPopulatedArray(arr) { return !!(isArray(arr) && arr.length > 0); @@ -473,7 +473,7 @@ function reloadViewabilityScriptWithCorrectParameters(bid) { if (viewJsPayload) { let prebidParams = 'pbjs_adid=' + bid.adId + ';pbjs_auc=' + bid.adUnitCode; - let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload) + let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload); let newJsTrackerSrc = jsTrackerSrc.replace('dom_id=%native_dom_id%', prebidParams); @@ -567,7 +567,7 @@ function formatRequest(payload, bidderRequest) { if (getParameterByName('apn_test').toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { options.customHeaders = { 'X-Is-Test': 1 - } + }; } if (payload.tags.length > MAX_IMPS_PER_REQUEST) { @@ -805,7 +805,7 @@ function bidToTag(bid) { tag.code = bid.params.invCode; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; let bidFloor = getBidFloor(bid); diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js index ec9aeb2b888..3915df8b976 100644 --- a/modules/boldwinBidAdapter.js +++ b/modules/boldwinBidAdapter.js @@ -57,7 +57,7 @@ export const spec = { let location; // TODO: this odd try-catch block was copied in several adapters; it doesn't seem to be correct for cross-origin try { - location = new URL(bidderRequest.refererInfo.page) + location = new URL(bidderRequest.refererInfo.page); winTop = window.top; } catch (e) { location = winTop.location; @@ -78,7 +78,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } const len = validBidRequests.length; @@ -91,10 +91,10 @@ export const spec = { if (mediaTypes) { if (mediaTypes[BANNER] && mediaTypes[BANNER].sizes) { placement.adFormat = BANNER; - sizes = mediaTypes[BANNER].sizes + sizes = mediaTypes[BANNER].sizes; } else if (mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize) { placement.adFormat = VIDEO; - sizes = mediaTypes[VIDEO].playerSize + sizes = mediaTypes[VIDEO].playerSize; placement.minduration = mediaTypes[VIDEO].minduration; placement.maxduration = mediaTypes[VIDEO].maxduration; placement.mimes = mediaTypes[VIDEO].mimes; diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index 4594b8c3ce6..9a0ff3a5422 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -105,8 +105,8 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL' - request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0 + request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL'; + request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; } } diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index cde32a2f1c0..2b18e59032c 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -304,4 +304,4 @@ function enableConsentManagement(configFromUser = false) { loadConsentData(); // immediately look up consent data to make it available without requiring an auction } config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement)); -setTimeout(() => !addedConsentHook && enableConsentManagement()) +setTimeout(() => !addedConsentHook && enableConsentManagement()); diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 9a5f5f4675b..f6c470d82a9 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -4,7 +4,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'consumable'; -const BASE_URI = 'https://e.serverbid.com/api/v2' +const BASE_URI = 'https://e.serverbid.com/api/v2'; let siteId = 0; let bidder = 'consumable'; diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index c7f843f421d..1c269b2bba4 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -503,7 +503,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { slot.video = video; } - enrichSlotWithFloors(slot, bidRequest) + enrichSlotWithFloors(slot, bidRequest); return slot; }), diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 40b79784595..d1743ac63af 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -282,7 +282,7 @@ export const spec = { ext: { gdpr: gdprConsent.gdprApplies ? 1 : 0 } - } + }; } if (uspConsent) { diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index b44f94f0495..bfe5f3952bc 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -208,7 +208,7 @@ export const spec = { ext: { gdpr: gdprConsent.gdprApplies ? 1 : 0 } - } + }; } if (uspConsent) { diff --git a/modules/hadronRtdProvider.js b/modules/hadronRtdProvider.js index a6aa0197d55..0bd4e6f8344 100644 --- a/modules/hadronRtdProvider.js +++ b/modules/hadronRtdProvider.js @@ -30,7 +30,7 @@ export const storage = getStorageManager({gvlid: AU_GVLID, moduleName: SUBMODULE */ const urlAddParams = (url, params) => { return url + (url.indexOf('?') > -1 ? '&' : '?') + params -} +}; /** * Deep set an object unless value present. diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index 19602c27093..6a10c2a94eb 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -159,7 +159,7 @@ export const merkleIdSubmodule = { if (typeof configParams.endpoint !== 'string') { logWarn('User ID - merkleId submodule endpoint string is not defined'); - configParams.endpoint = ID_URL + configParams.endpoint = ID_URL; } if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { @@ -188,7 +188,7 @@ export const merkleIdSubmodule = { refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > refreshInSeconds * 1000); if (refreshNeeded) { logInfo('User ID - merkleId needs refreshing id'); - const resp = generateId(configParams, configStorage) + const resp = generateId(configParams, configStorage); return {callback: resp}; } } diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index b2eaf96f414..1b31000df43 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -33,7 +33,7 @@ const AUDIENCE_IDS = [ {type: 13, bidKey: 'userId.idl_env'}, {type: 14, bidKey: 'userId.criteoId'}, {type: 15, bidKey: 'userId.pubcid'} -] +]; function createCBT() { const randomValue = Math.floor(Math.random() * Math.pow(10, 18)).toString(16); diff --git a/modules/mobfoxpbBidAdapter.js b/modules/mobfoxpbBidAdapter.js index df900318881..9ff50e2531f 100644 --- a/modules/mobfoxpbBidAdapter.js +++ b/modules/mobfoxpbBidAdapter.js @@ -69,7 +69,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } @@ -82,7 +82,7 @@ export const spec = { schain: bid.schain || {}, bidfloor: getBidFloor(bid) }; - const mediaType = bid.mediaTypes + const mediaType = bid.mediaTypes; if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { placement.traffic = BANNER; diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index e0630c7e412..f0ed3c24a31 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -209,6 +209,6 @@ export const spec = { onBidWon, getWindowContext, onTimeout -} +}; registerBidder(spec); diff --git a/modules/operaadsBidAdapter.js b/modules/operaadsBidAdapter.js index b75090cdec6..aa548debf32 100644 --- a/modules/operaadsBidAdapter.js +++ b/modules/operaadsBidAdapter.js @@ -549,7 +549,7 @@ function createImp(bidRequest) { const floorDetail = getBidFloor(bidRequest, { mediaType: mediaType || '*', size: size || '*' - }) + }); impItem.bidfloor = floorDetail.floor; impItem.bidfloorcur = floorDetail.currency; diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 2f229720208..3e59c2c8def 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -16,7 +16,7 @@ import {getPriceBucketString} from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; import {getRefererInfo} from '../src/refererDetection.js'; const BIDDER_CODE = 'ozone'; -const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & cookie +const ORIGIN = 'https://elb.the-ozone-project.com'; // applies only to auction & cookie const AUCTIONURI = '/openrtb2/auction'; const OZONECOOKIESYNC = '/static/load-cookie.html'; const OZONE_RENDERER_URL = 'https://prebid.the-ozone-project.com/ozone-renderer.js'; @@ -98,7 +98,7 @@ export const spec = { this.loadWhitelabelData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code - let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED' + let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED'; if (!(bid.params.hasOwnProperty('placementId'))) { logError(err1.replace('{param}', 'placementId'), adUnitCode); return false; diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 48b570a683b..38bcc4521be 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -384,7 +384,7 @@ function bidResponseHandler(args) { if (bid.bidder && args.bidderCode && bid.bidder !== args.bidderCode) { bid = copyRequiredBidDetails(args); - cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId].push(bid) + cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId].push(bid); } bid.adId = args.adId; bid.source = formatSource(bid.source || args.source); diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index bdc8010618c..922fedd5092 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -608,7 +608,7 @@ function _addDealCustomTargetings(imp, bid) { if (dctr.substring(dctrLen, dctrLen - 1) === '|') { dctr = dctr.substring(0, dctrLen - 1); } - imp.ext['key_val'] = dctr.trim() + imp.ext['key_val'] = dctr.trim(); } else { logWarn(LOG_WARN_PREFIX + 'Ignoring param : dctr with value : ' + dctr + ', expects string-value, found empty or non-string value'); } @@ -811,7 +811,7 @@ function _checkMediaType(bid, newBid) { if (bid.ext && bid.ext['bidtype'] != undefined) { newBid.mediaType = MEDIATYPE[bid.ext.bidtype]; } else { - logInfo(LOG_WARN_PREFIX + 'bid.ext.bidtype does not exist, checking alternatively for mediaType') + logInfo(LOG_WARN_PREFIX + 'bid.ext.bidtype does not exist, checking alternatively for mediaType'); var adm = bid.adm; var admStr = ''; var videoRegex = new RegExp(/VAST\s+version/); @@ -1097,7 +1097,7 @@ export const spec = { if (bidderRequest && allowAlternateBidder == true) { let allowedBiddersList = bidderSettings.get(bidderRequest.bidderCode, 'allowedAlternateBidderCodes'); if (isArray(allowedBiddersList)) { - allowedBiddersList = allowedBiddersList.map(val => val.trim().toLowerCase()).filter(val => !!val).filter(uniques) + allowedBiddersList = allowedBiddersList.map(val => val.trim().toLowerCase()).filter(val => !!val).filter(uniques); biddersList = allowedBiddersList.includes('*') ? allBiddersList : [...biddersList, ...allowedBiddersList]; } else { biddersList = allBiddersList; @@ -1168,7 +1168,7 @@ export const spec = { mergeDeep(payload, {user: commonFpd.user}); } if (commonFpd.bcat) { - blockedIabCategories = blockedIabCategories.concat(commonFpd.bcat) + blockedIabCategories = blockedIabCategories.concat(commonFpd.bcat); } if (commonFpd.ext?.prebid?.bidderparams?.[bidderRequest.bidderCode]?.acat) { const acatParams = commonFpd.ext.prebid.bidderparams[bidderRequest.bidderCode].acat; diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 7b66bb7600f..52931be0078 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -96,7 +96,7 @@ function bidResponseAvailable(request, response) { const idToImpMap = {}; const idToBidMap = {}; const idToSlotConfig = {}; - const bidResponse = response.body + const bidResponse = response.body; // extract the request bids and the response bids, keyed by impr-id const ortbRequest = request.data; ortbRequest.imp.forEach(imp => { diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index c9e9fc00eb7..2e6097dc100 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -71,11 +71,11 @@ function buildRequests(validBidRequests, bidderRequest) { } if (!bidder) { - bidder = bidRequest.bidder + bidder = bidRequest.bidder; } if (!bidder) { - bidder = bidRequest.bidder + bidder = bidRequest.bidder; } if (!count) { diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 504de2e209a..7d4049bf1a2 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -384,7 +384,7 @@ function generateGeneralParams(generalObject, bidderRequest) { ua: navigator.userAgent, session_id: getBidIdParameter('auctionId', generalObject), tmax: timeout - } + }; const userIdsParam = getBidIdParameter('userId', generalObject); if (userIdsParam) { @@ -430,5 +430,5 @@ function generateGeneralParams(generalObject, bidderRequest) { generalParams.page_url = deepAccess(bidderRequest, 'refererInfo.page') || deepAccess(window, 'location.href'); } - return generalParams + return generalParams; } diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 192d5406b86..4817d62348a 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -939,7 +939,7 @@ function parseSizes(bid, mediaType) { } else if (typeof deepAccess(bid, 'mediaTypes.banner.sizes') !== 'undefined') { sizes = mapSizes(bid.mediaTypes.banner.sizes); } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { - sizes = mapSizes(bid.sizes) + sizes = mapSizes(bid.sizes); } else { logWarn('Rubicon: no sizes are setup or found'); } @@ -1045,7 +1045,7 @@ function applyFPD(bidRequest, mediaType, data) { let val = validate(obj, key, name); let loc = (MAP[key] && isParent) ? `${MAP[key]}` : (key === 'data') ? `${MAP[name]}iab` : `${MAP[name]}${key}`; data[loc] = (data[loc]) ? data[loc].concat(',', val) : val; - } + }; if (mediaType === BANNER) { ['site', 'user'].forEach(name => { diff --git a/modules/sirdataRtdProvider.js b/modules/sirdataRtdProvider.js index 532d938375e..369276e7638 100644 --- a/modules/sirdataRtdProvider.js +++ b/modules/sirdataRtdProvider.js @@ -200,7 +200,7 @@ export function addSegmentData(reqBids, data, moduleConfig, onDone) { if (typeof n.setTargeting !== 'undefined' && sirdataMergedList && sirdataMergedList.length > 0) { n.setTargeting('sd_rtd', sirdataMergedList); } - }) + }); } catch (e) { logError(e); } @@ -224,7 +224,7 @@ export function addSegmentData(reqBids, data, moduleConfig, onDone) { curationData = {'segments': [], 'categories': []}; sirdataMergedList = []; - let minScore = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('contextualMinRelevancyScore') ? moduleConfig.params.bidders[bidderIndex].contextualMinRelevancyScore : globalMinScore) + let minScore = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('contextualMinRelevancyScore') ? moduleConfig.params.bidders[bidderIndex].contextualMinRelevancyScore : globalMinScore); if (!biddersParamsExist || (indexFound && (!moduleConfig.params.bidders[bidderIndex].hasOwnProperty('adUnitCodes') || moduleConfig.params.bidders[bidderIndex].adUnitCodes.indexOf(adUnit.code) !== -1))) { switch (bid.bidder) { @@ -476,7 +476,7 @@ export function addSegmentData(reqBids, data, moduleConfig, onDone) { } } } catch (e) { - logError(e) + logError(e); } }) }); diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index f3188c1f110..e5f71265e34 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -221,7 +221,7 @@ export const spec = { name: provider, value: targetingstring, } - }) + }); } } @@ -230,7 +230,7 @@ export const spec = { requestPayload.user = { ext: userExt, data: targetingarr - } + }; } return { diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 0a19410c78f..ffe2bef054c 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -338,7 +338,7 @@ const mapImpression = slot => { if (!adUnitsCalled[adUnitCode]) { // this is a new adunit - assign & save pbsize adSizesCalled[slotSize] = adSizesCalled[slotSize] ? adSizesCalled[slotSize] += 1 : 1; - adUnitsCalled[adUnitCode] = `${slotSize}_${adSizesCalled[slotSize]}` + adUnitsCalled[adUnitCode] = `${slotSize}_${adSizesCalled[slotSize]}`; } ext.data = Object.assign({ pbsize: adUnitsCalled[adUnitCode] }, ext.data); @@ -682,7 +682,7 @@ const spec = { site: site.id, slot: site.slot, cpm: bid.cpm.toPrecision(4), - } + }; const jsTracker = ''; // banner + break; + default: ''; break; + }; + + return ADM_PAYLOAD; +}; + +const generateResponseMock = (admPayloadType) => { + const bidResponse = { + id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9', + impid: '274395c06a24e5', + adm: generateAdmPayload(admPayloadType), + price: 1, + w: 300, + h: 250, + crid: 'ssp-placement-name', + adomain: ['advertiser-domain.com'] + }; + + const serverResponse = { + body: { + id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9', + seatbid: [{ bid: [ bidResponse ], seat: 13107 }] + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + + return {serverResponse, data, bidderRequest}; +} + +// Unit tests +describe('adtrgtme Bid Adapter:', () => { + it('PLACEHOLDER TO PASS GULP', () => { + const obj = {}; + expect(obj).to.be.an('object'); + }); + + describe('Validate basic properties', () => { + it('should define the correct bidder code', () => { + expect(spec.code).to.equal('adtrgtme') + }); + }); + + describe('getUserSyncs()', () => { + const IMAGE_PIXEL_URL = 'http://image-pixel.com/foo/bar?1234&baz=true'; + const IFRAME_ONE_URL = 'http://image-iframe.com/foo/bar?1234&baz=true'; + const IFRAME_TWO_URL = 'http://image-iframe-two.com/foo/bar?1234&baz=true'; + + let serverResponses = []; + beforeEach(() => { + serverResponses[0] = { + body: { + ext: { + pixels: `` + } + } + } + }); + + after(() => { + serverResponses = undefined; + }); + + it('for only iframe enabled syncs', () => { + let syncOptions = { + iframeEnabled: true, + pixelEnabled: false + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(2); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'iframe', 'url': IFRAME_ONE_URL}, + {type: 'iframe', 'url': IFRAME_TWO_URL} + ] + ) + }); + + it('for only pixel enabled syncs', () => { + let syncOptions = { + iframeEnabled: false, + pixelEnabled: true + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(1); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'image', 'url': IMAGE_PIXEL_URL} + ] + ) + }); + + it('for both pixel and iframe enabled syncs', () => { + let syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(3); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'iframe', 'url': IFRAME_ONE_URL}, + {type: 'image', 'url': IMAGE_PIXEL_URL}, + {type: 'iframe', 'url': IFRAME_TWO_URL} + ] + ) + }); + }); + + // Validate Bid Requests + describe('isBidRequestValid()', () => { + const INVALID_INPUT = [ + {}, + {params: {}}, + {params: {sid: '1234', zid: '4321'}}, + {params: {sid: '1220291391', zid: 4321}}, + {params: {zid: ''}}, + {params: {sid: '', zid: ''}}, + ]; + + INVALID_INPUT.forEach(input => { + it(`should determine that the bid is INVALID for the input ${JSON.stringify(input)}`, () => { + expect(spec.isBidRequestValid(input)).to.be.false; + }); + }); + + it('should determine that the bid is VALID if sid and zid are present on the params object', () => { + const validBid = { + params: { + sid: 1220291391, + zid: 1836455615 + } + }; + expect(spec.isBidRequestValid(validBid)).to.be.true; + }); + }); + + describe('Price Floor module support:', () => { + it('should get bidfloor from getFloor method', () => { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + bidRequest.params.bidOverride = {cur: 'EUR'}; + bidRequest.getFloor = (floorObj) => { + return { + floor: bidRequest.floors.values[floorObj.mediaType + '|300x250'], + currency: floorObj.currency, + mediaType: floorObj.mediaType + } + }; + bidRequest.floors = { + currency: 'EUR', + values: { + 'banner|300x250': 5.55 + } + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.cur).to.deep.equal(['EUR']); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(5.55); + }); + }); + + describe('Schain module support:', () => { + it('should send Global or Bidder specific schain', function () { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const globalSchain = { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'some-platform.com', + sid: '111111', + rid: bidRequest.bidId, + hp: 1 + }] + }; + bidRequest.schain = globalSchain; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const schain = data.source.ext.schain; + expect(schain.nodes.length).to.equal(1); + expect(schain).to.equal(globalSchain); + }); + }); + + describe('First party data module - "Site" support (ortb2):', () => { + // Should not allow invalid "site" data types + const INVALID_ORTB2_TYPES = [ null, [], 123, 'unsupportedKeyName', true, false, undefined ]; + INVALID_ORTB2_TYPES.forEach(param => { + it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { site: param } + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.be.undefined; + }); + }); + + // Should add valid "site" params + const VALID_SITE_STRINGS = ['name', 'domain', 'page', 'ref', 'keywords']; + const VALID_SITE_ARRAYS = ['cat', 'sectioncat', 'pagecat']; + + VALID_SITE_STRINGS.forEach(param => { + it(`should allow supported site keys to be added bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + [param]: 'something' + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.exist; + expect(data.site[param]).to.be.a('string'); + expect(data.site[param]).to.be.equal(ortb2.site[param]); + }); + }); + + VALID_SITE_ARRAYS.forEach(param => { + it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + [param]: ['something'] + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.exist; + expect(data.site[param]).to.be.a('array'); + expect(data.site[param]).to.be.equal(ortb2.site[param]); + }); + }); + + // Should not allow invalid "site.content" data types + INVALID_ORTB2_TYPES.forEach(param => { + it(`should determine that the ortb2.site.content key is invalid and should not be added to bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: param + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content).to.be.undefined; + }); + }); + + // Should not allow invalid "site.content" keys + it(`should not allow invalid ortb2.site.content object keys to be added to bid-request: {custom object}`, () => { + const ortb2 = { + site: { + content: { + fake: 'news', + unreal: 'param', + counterfit: 'data' + } + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content).to.be.a('object'); + }); + + // Should append valid "site.content" keys + const VALID_CONTENT_STRINGS = ['id', 'title', 'language']; + VALID_CONTENT_STRINGS.forEach(param => { + it(`should determine that the ortb2.site String key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: { + [param]: 'something' + } + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content[param]).to.exist; + expect(data.site.content[param]).to.be.a('string'); + expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + }); + }); + + const VALID_CONTENT_ARRAYS = ['cat']; + VALID_CONTENT_ARRAYS.forEach(param => { + it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: { + [param]: ['something', 'something-else'] + } + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content[param]).to.be.a('array'); + expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + }); + }); + }); + + describe('First party data module - "User" support (ortb2):', () => { + // Global ortb2.user validations + // Should not allow invalid "user" data types + const INVALID_ORTB2_TYPES = [ null, [], 'unsupportedKeyName', true, false, undefined ]; + INVALID_ORTB2_TYPES.forEach(param => { + it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { user: param } + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.be.undefined; + }); + }); + + // Should add valid "user" params + const VALID_USER_STRINGS = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; + VALID_USER_STRINGS.forEach(param => { + it(`should allow supported user string keys to be added bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + [param]: 'something' + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.exist; + expect(data.user[param]).to.be.a('string'); + expect(data.user[param]).to.be.equal(ortb2.user[param]); + }); + }); + + const VALID_USER_OBJECTS = ['ext']; + VALID_USER_OBJECTS.forEach(param => { + it(`should allow supported user extObject keys to be added to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + [param]: {a: '123', b: '456'} + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.exist; + expect(data.user[param]).to.be.a('object'); + expect(data.user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}}); + config.setConfig({ortb2: {}}); + }); + }); + + // adUnit.ortb2Imp.ext.data + it(`should allow adUnit.ortb2Imp.ext.data object to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + ext: { + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } + } + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].ext.data).to.deep.equal(validBidRequests[0].ortb2Imp.ext.data); + }); + // adUnit.ortb2Imp.instl + it(`should allow adUnit.ortb2Imp.instl numeric boolean "1" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: 1 + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.deep.equal(validBidRequests[0].ortb2Imp.instl); + }); + + it(`should prevent adUnit.ortb2Imp.instl boolean "true" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: true + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.not.exist; + }); + + it(`should prevent adUnit.ortb2Imp.instl boolean "false" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: false + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.not.exist; + }); + }); + + describe('GDPR & Consent:', () => { + it('should return request objects that do not send cookies if purpose 1 consent is not provided', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + bidderRequest.gdprConsent = { + consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA', + apiVersion: 2, + vendorData: { + purpose: { + consents: { + '1': false + } + } + }, + gdprApplies: true + }; + const options = spec.buildRequests(validBidRequests, bidderRequest)[0].options; + expect(options.withCredentials).to.be.false; + }); + }); + + describe('Endpoint & Impression Request Mode:', () => { + it('should route request to config override endpoint', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const sid = validBidRequests[0].params.sid; + const testOverrideEndpoint = 'http://new_bidder_host.com/ssp?s='; + config.setConfig({ + adtrgtme: { + endpoint: testOverrideEndpoint + } + }); + const response = spec.buildRequests(validBidRequests, bidderRequest)[0]; + expect(response).to.deep.include( + { + method: 'POST', + url: testOverrideEndpoint + sid + }); + }); + + it('should route request to endpoint + sid', () => { + config.setConfig({ + adtrgtme: {} + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const sid = validBidRequests[0].params.sid; + const response = spec.buildRequests(validBidRequests, bidderRequest); + expect(response[0]).to.deep.include({ + method: 'POST', + url: 'https://z.cdn.adtarget.market/ssp?prebid&s=' + sid + }); + }); + + it('should return a single request object for single request mode', () => { + let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const BID_ID_2 = '84ab50xxxxx'; + const BID_ZID_2 = 98876543210; + const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; + const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, zid: BID_ZID_2, adUnitCode: AD_UNIT_CODE_2}); + validBidRequests = [bidRequest, bidRequest2]; + bidderRequest.bids = validBidRequests; + + config.setConfig({ + adtrgtme: { + singleRequestMode: true + } + }); + + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + + expect(data.imp).to.be.an('array').with.lengthOf(2); + + expect(data.imp[0]).to.deep.include({ + id: DEFAULT_BID_ID, + ext: { + dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE + } + }); + + expect(data.imp[1]).to.deep.include({ + id: BID_ID_2, + tagid: BID_ZID_2, + ext: { + dfp_ad_unit_code: AD_UNIT_CODE_2 + } + }); + }); + }); + + describe('Validate request filtering:', () => { + it('should not return request when no bids are present', function () { + let request = spec.buildRequests([]); + expect(request).to.be.undefined; + }); + + it('buildRequests(): should return an array with the correct amount of request objects', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const response = spec.buildRequests(validBidRequests, bidderRequest).bidderRequest; + expect(response.bids).to.be.an('array').to.have.lengthOf(1); + }); + }); + + describe('Request Headers validation:', () => { + it('should return request objects with the relevant custom headers and content type declaration', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + bidderRequest.gdprConsent.gdprApplies = false; + const options = spec.buildRequests(validBidRequests, bidderRequest).options; + expect(options).to.deep.equal( + { + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': '2.5' + }, + withCredentials: true + }); + }); + }); + + describe('Request Payload oRTB bid validation:', () => { + it('should generate a valid openRTB bid-request object in the data field', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.site).to.deep.include({ + id: bidderRequest.bids[0].params.sid, + page: bidderRequest.refererInfo.page + }); + + expect(data.device).to.deep.equal({ + dnt: 0, + ua: navigator.userAgent, + ip: undefined + }); + + expect(data.regs).to.deep.equal({ + ext: { + 'us_privacy': '', + gdpr: 1 + } + }); + + expect(data.source).to.deep.equal({ + ext: { + hb: 1, + adapterver: ADAPTER_VERSION, + prebidver: PREBID_VERSION, + integration: { + name: INTEGRATION_METHOD, + ver: PREBID_VERSION + } + }, + fd: 1 + }); + + expect(data.cur).to.deep.equal(['USD']); + }); + + it('should generate a valid openRTB imp.ext object in the bid-request', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const bid = validBidRequests[0]; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.imp[0].ext).to.deep.equal({ + dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE + }); + }); + + it('should use siteId value as site.id in the outbound bid-request when using "pubId" integration mode', () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); + validBidRequests[0].params.sid = 9876543210; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.site.id).to.equal(9876543210); + }); + + it('should use placementId value as imp.tagid in the outbound bid-request when using "zid"', () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}), + TEST_ZID = 54321; + validBidRequests[0].params.zid = TEST_ZID; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.imp[0].tagid).to.deep.equal(TEST_ZID); + }); + }); + + describe('Request Payload oRTB bid.imp validation:', () => { + // Validate Banner imp imp when adtrgtme.mode=undefined + it('should generate a valid "Banner" imp object', () => { + config.setConfig({ + adtrgtme: {} + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}] + }); + }); + + // Validate Banner imp + it('should generate a valid "Banner" imp object', () => { + config.setConfig({ + adtrgtme: { mode: 'banner' } + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}] + }); + }); + }); + + describe('interpretResponse()', () => { + describe('for mediaTypes: "banner"', () => { + it('should insert banner payload into response[0].ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].mediaType).to.equal('banner'); + }) + }); + + describe('Support Advertiser domains', () => { + it('should append bid-response adomain to meta.advertiserDomains', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].meta.advertiserDomains).to.be.a('array'); + expect(response[0].meta.advertiserDomains[0]).to.equal('advertiser-domain.com'); + }) + }); + + describe('bid response Ad ID / Creative ID', () => { + it('should use adId if it exists in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const adId = 'bid-response-adId'; + serverResponse.body.seatbid[0].bid[0].adId = adId; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(adId); + }); + + it('should use impid if adId does not exist in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const impid = '25b6c429c1f52f'; + serverResponse.body.seatbid[0].bid[0].impid = impid; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(impid); + }); + + it('should use crid if adId & impid do not exist in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const crid = 'passback-12579'; + serverResponse.body.seatbid[0].bid[0].impid = undefined; + serverResponse.body.seatbid[0].bid[0].crid = crid; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(crid); + }); + }); + + describe('Time To Live (ttl)', () => { + const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; + UNSUPPORTED_TTL_FORMATS.forEach(param => { + it('should not allow unsupported global adtrgtme.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + adtrgtme: { ttl: param } + }); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + + it('should not allow unsupported params.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + }); + + const UNSUPPORTED_TTL_VALUES = [-1, 3601]; + UNSUPPORTED_TTL_VALUES.forEach(param => { + it('should not allow invalid global adtrgtme.ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + adtrgtme: { ttl: param } + }); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + + it('should not allow invalid params.ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + }); + + it('should give presedence to Gloabl ttl over params.ttl ', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + adtrgtme: { ttl: 500 } + }); + bidderRequest.bids[0].params.ttl = 400; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(500); + }); + }); + + describe('Aliasing support', () => { + it('should return undefined as the bidder code value', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].bidderCode).to.be.undefined; + }); + }); + }); +}); From 8a47629bbe1bd304fcb0739090fc77d95af0961b Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 11 Aug 2022 09:13:26 -0700 Subject: [PATCH 137/569] update pre version (#8823) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3bbc12d2d5..19748040d79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.10.0-pre", + "version": "7.11.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 941f0daffa39f71615433020239a05a3ad2c6302 Mon Sep 17 00:00:00 2001 From: Olivier Date: Thu, 11 Aug 2022 19:29:04 +0200 Subject: [PATCH 138/569] AdagioBidAdapter: use `refererInfo.topmostLocation` only (#8824) --- modules/adagioBidAdapter.js | 5 ++--- test/spec/modules/adagioBidAdapter_spec.js | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 5c07cbe2844..ac01b317ab3 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -271,9 +271,8 @@ function getDevice() { function getSite(bidderRequest) { const { refererInfo } = bidderRequest; return { - // TODO: do these fallbacks make sense? - domain: refererInfo.domain || parseDomain(refererInfo.topmostLocation) || '', - page: refererInfo.page || refererInfo.topmostLocation || '', + domain: parseDomain(refererInfo.topmostLocation) || '', + page: refererInfo.topmostLocation || '', referrer: refererInfo.ref || getWindowSelf().document.referrer || '', top: refererInfo.reachedTop }; diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 15f79b407d1..36bb5fb1e4d 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1391,6 +1391,7 @@ describe('Adagio bid adapter', () => { refererInfo: { numIframes: 0, reachedTop: true, + topmostLocation: 'https://test.io/article/a.html', page: 'https://test.io/article/a.html', domain: 'test.io', ref: 'https://google.com' @@ -1417,6 +1418,7 @@ describe('Adagio bid adapter', () => { numIframes: 0, reachedTop: true, page: 'http://level.io/', + topmostLocation: 'http://level.io/', stack: [ 'http://level.io/', 'http://example.com/iframe1.html', From 0e3dd1c31a4e81e22ee6dd9db46f7f7776c81eb7 Mon Sep 17 00:00:00 2001 From: Gena Date: Fri, 12 Aug 2022 19:25:03 +0200 Subject: [PATCH 139/569] Add OCM alias (#8814) --- modules/adtelligentBidAdapter.js | 4 +++- test/spec/modules/adtelligentBidAdapter_spec.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 6c6fca13d7b..03d4e634062 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -22,6 +22,7 @@ const HOST_GETTERS = { streamkey: () => 'ghb.hb.streamkey.net', janet: () => 'ghb.bidder.jmgads.com', pgam: () => 'ghb.pgamssp.com', + ocm: () => 'ghb.cenarius.orangeclickmedia.com', } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -40,7 +41,8 @@ export const spec = { aliases: ['onefiftytwomedia', 'appaloosa', 'bidsxchange', 'streamkey', 'janet', { code: 'selectmedia', gvlid: 775 }, { code: 'navelix', gvlid: 380 }, - 'pgam' + 'pgam', + 'ocm', ], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 2600b7c4595..a9b9724da3a 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -19,6 +19,7 @@ const aliasEP = { streamkey: 'https://ghb.hb.streamkey.net/v2/auction/', janet: 'https://ghb.bidder.jmgads.com/v2/auction/', pgam: 'https://ghb.pgamssp.com/v2/auction/', + ocm: 'https://ghb.cenarius.orangeclickmedia.com/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; From b3710cbfa19591e9c0ef31c43e257e152693c896 Mon Sep 17 00:00:00 2001 From: Brian Schmidt Date: Fri, 12 Aug 2022 14:42:55 -0700 Subject: [PATCH 140/569] make openxAnalyticsAdapter do nothing (#8828) --- modules/openxAnalyticsAdapter.js | 749 +----------------- modules/openxAnalyticsAdapter.md | 4 + .../modules/openxAnalyticsAdapter_spec.js | 644 +-------------- 3 files changed, 15 insertions(+), 1382 deletions(-) diff --git a/modules/openxAnalyticsAdapter.js b/modules/openxAnalyticsAdapter.js index 7cc71fe898e..3c5517223af 100644 --- a/modules/openxAnalyticsAdapter.js +++ b/modules/openxAnalyticsAdapter.js @@ -1,128 +1,19 @@ import { - _each, - _map, - deepAccess, - flatten, - getWindowLocation, - isEmpty, - logError, - logInfo, - logMessage, - logWarn, - parseQS, - parseSizesInput, - uniques + logWarn } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; -import {ajax} from '../src/ajax.js'; -import {find, includes} from '../src/polyfill.js'; -export const AUCTION_STATES = { - INIT: 'initialized', // auction has initialized - ENDED: 'ended', // all auction requests have been accounted for - COMPLETED: 'completed' // all slots have rendered -}; - -const ADAPTER_VERSION = '0.1'; -const SCHEMA_VERSION = '0.1'; - -const AUCTION_END_WAIT_TIME = 1000; -const URL_PARAM = ''; -const ANALYTICS_TYPE = 'endpoint'; -const ENDPOINT = 'https://prebid.openx.net/ox/analytics/'; - -// Event Types -const { - EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, AUCTION_END, BID_WON } -} = CONSTANTS; -const SLOT_LOADED = 'slotOnload'; - -const UTM_TAGS = [ - 'utm_campaign', - 'utm_source', - 'utm_medium', - 'utm_term', - 'utm_content' -]; -const UTM_TO_CAMPAIGN_PROPERTIES = { - 'utm_campaign': 'name', - 'utm_source': 'source', - 'utm_medium': 'medium', - 'utm_term': 'term', - 'utm_content': 'content' -}; - -/** - * @typedef {Object} OxAnalyticsConfig - * @property {string} orgId - * @property {string} publisherPlatformId - * @property {number} publisherAccountId - * @property {string} configId - * @property {string} optimizerConfig - * @property {number} sampling - * @property {Object} campaign - * @property {number} payloadWaitTime - * @property {number} payloadWaitTimePadding - * @property {Array} adUnits - */ - -/** - * @type {OxAnalyticsConfig} - */ -const DEFAULT_ANALYTICS_CONFIG = { - orgId: void (0), - publisherPlatformId: void (0), - publisherAccountId: void (0), - sampling: 0.05, // default sampling rate of 5% - testCode: 'default', - campaign: {}, - adUnits: [], - payloadWaitTime: AUCTION_END_WAIT_TIME, - payloadWaitTimePadding: 2000 -}; - -// Initialization -/** - * @type {OxAnalyticsConfig} - */ -let analyticsConfig; -let auctionMap = {}; -let auctionOrder = 1; // tracks the number of auctions ran on the page - -let googletag = window.googletag || {}; -googletag.cmd = googletag.cmd || []; - -let openxAdapter = Object.assign(adapter({ urlParam: URL_PARAM, analyticsType: ANALYTICS_TYPE })); +let openxAdapter = Object.assign(adapter({ urlParam: '', analyticsType: 'endpoint' })); openxAdapter.originEnableAnalytics = openxAdapter.enableAnalytics; openxAdapter.enableAnalytics = function(adapterConfig = {options: {}}) { - if (isValidConfig(adapterConfig)) { - analyticsConfig = {...DEFAULT_ANALYTICS_CONFIG, ...adapterConfig.options}; - - // campaign properties defined by config will override utm query parameters - analyticsConfig.campaign = {...buildCampaignFromUtmCodes(), ...analyticsConfig.campaign}; - - logInfo('OpenX Analytics enabled with config', analyticsConfig); + logWarn('OpenX Analytics has been deprecated, this adapter will be removed in Prebid 8'); - // override track method with v2 handlers - openxAdapter.track = prebidAnalyticsEventHandler; + openxAdapter.track = prebidAnalyticsEventHandler; - googletag.cmd.push(function () { - let pubads = googletag.pubads(); - - if (pubads.addEventListener) { - pubads.addEventListener(SLOT_LOADED, args => { - openxAdapter.track({eventType: SLOT_LOADED, args}); - logInfo('OX: SlotOnLoad event triggered'); - }); - } - }); - - openxAdapter.originEnableAnalytics(adapterConfig); - } + openxAdapter.originEnableAnalytics(adapterConfig); }; adapterManager.registerAnalyticsAdapter({ @@ -132,635 +23,5 @@ adapterManager.registerAnalyticsAdapter({ export default openxAdapter; -/** - * Test Helper Functions - */ - -// reset the cache for unit tests -openxAdapter.reset = function() { - auctionMap = {}; - auctionOrder = 1; -}; - -/** - * Private Functions - */ - -function isValidConfig({options: analyticsOptions}) { - let hasOrgId = analyticsOptions && analyticsOptions.orgId !== void (0); - - const fieldValidations = [ - // tuple of property, type, required - ['orgId', 'string', hasOrgId], - ['publisherPlatformId', 'string', !hasOrgId], - ['publisherAccountId', 'number', !hasOrgId], - ['configId', 'string', false], - ['optimizerConfig', 'string', false], - ['sampling', 'number', false], - ['adIdKey', 'string', false], - ['payloadWaitTime', 'number', false], - ['payloadWaitTimePadding', 'number', false], - ]; - - let failedValidation = find(fieldValidations, ([property, type, required]) => { - // if required, the property has to exist - // if property exists, type check value - return (required && !analyticsOptions.hasOwnProperty(property)) || - /* eslint-disable valid-typeof */ - (analyticsOptions.hasOwnProperty(property) && typeof analyticsOptions[property] !== type); - }); - if (failedValidation) { - let [property, type, required] = failedValidation; - - if (required) { - logError(`OpenXAnalyticsAdapter: Expected '${property}' to exist and of type '${type}'`); - } else { - logError(`OpenXAnalyticsAdapter: Expected '${property}' to be type '${type}'`); - } - } - - return !failedValidation; -} - -function buildCampaignFromUtmCodes() { - const location = getWindowLocation(); - const queryParams = parseQS(location && location.search); - let campaign = {}; - - UTM_TAGS.forEach(function(utmKey) { - let utmValue = queryParams[utmKey]; - if (utmValue) { - let key = UTM_TO_CAMPAIGN_PROPERTIES[utmKey]; - campaign[key] = decodeURIComponent(utmValue); - } - }); - return campaign; -} - -function detectMob() { - if ( - navigator.userAgent.match(/Android/i) || - navigator.userAgent.match(/webOS/i) || - navigator.userAgent.match(/iPhone/i) || - navigator.userAgent.match(/iPad/i) || - navigator.userAgent.match(/iPod/i) || - navigator.userAgent.match(/BlackBerry/i) || - navigator.userAgent.match(/Windows Phone/i) - ) { - return true; - } else { - return false; - } -} - -function detectOS() { - if (navigator.userAgent.indexOf('Android') != -1) return 'Android'; - if (navigator.userAgent.indexOf('like Mac') != -1) return 'iOS'; - if (navigator.userAgent.indexOf('Win') != -1) return 'Windows'; - if (navigator.userAgent.indexOf('Mac') != -1) return 'Macintosh'; - if (navigator.userAgent.indexOf('Linux') != -1) return 'Linux'; - if (navigator.appVersion.indexOf('X11') != -1) return 'Unix'; - return 'Others'; -} - -function detectBrowser() { - var isChrome = - /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor); - var isCriOS = navigator.userAgent.match('CriOS'); - var isSafari = - /Safari/.test(navigator.userAgent) && - /Apple Computer/.test(navigator.vendor); - var isFirefox = /Firefox/.test(navigator.userAgent); - var isIE = - /Trident/.test(navigator.userAgent) || /MSIE/.test(navigator.userAgent); - var isEdge = /Edge/.test(navigator.userAgent); - if (isIE) return 'Internet Explorer'; - if (isEdge) return 'Microsoft Edge'; - if (isCriOS) return 'Chrome'; - if (isSafari) return 'Safari'; - if (isFirefox) return 'Firefox'; - if (isChrome) return 'Chrome'; - return 'Others'; -} - function prebidAnalyticsEventHandler({eventType, args}) { - logMessage(eventType, Object.assign({}, args)); - switch (eventType) { - case AUCTION_INIT: - onAuctionInit(args); - break; - case BID_REQUESTED: - onBidRequested(args); - break; - case BID_RESPONSE: - onBidResponse(args); - break; - case BID_TIMEOUT: - onBidTimeout(args); - break; - case AUCTION_END: - onAuctionEnd(args); - break; - case BID_WON: - onBidWon(args); - break; - case SLOT_LOADED: - onSlotLoadedV2(args); - break; - } -} - -/** - * @typedef {Object} PbAuction - * @property {string} auctionId - Auction ID of the request this bid responded to - * @property {number} timestamp //: 1586675964364 - * @property {number} auctionEnd - timestamp of when auction ended //: 1586675964364 - * @property {string} auctionStatus //: "inProgress" - * @property {Array} adUnits //: [{…}] - * @property {string} adUnitCodes //: ["video1"] - * @property {string} labels //: undefined - * @property {Array} bidderRequests //: (2) [{…}, {…}] - * @property {Array} noBids //: [] - * @property {Array} bidsReceived //: [] - * @property {Array} winningBids //: [] - * @property {number} timeout //: 3000 - * @property {Object} config //: {publisherPlatformId: "a3aece0c-9e80-4316-8deb-faf804779bd1", publisherAccountId: 537143056, sampling: 1}/* - */ - -function onAuctionInit({auctionId, timestamp: startTime, timeout, adUnitCodes}) { - auctionMap[auctionId] = { - id: auctionId, - startTime, - endTime: void (0), - timeout, - auctionOrder, - userIds: [], - adUnitCodesCount: adUnitCodes.length, - adunitCodesRenderedCount: 0, - state: AUCTION_STATES.INIT, - auctionSendDelayTimer: void (0), - }; - - // setup adunit properties in map - auctionMap[auctionId].adUnitCodeToAdUnitMap = adUnitCodes.reduce((obj, adunitCode) => { - obj[adunitCode] = { - code: adunitCode, - adPosition: void (0), - bidRequestsMap: {} - }; - return obj; - }, {}); - - auctionOrder++; -} - -/** - * @typedef {Object} PbBidRequest - * @property {string} auctionId - Auction ID of the request this bid responded to - * @property {number} auctionStart //: 1586675964364 - * @property {Object} refererInfo - * @property {PbBidderRequest} bids - * @property {number} start - Start timestamp of the bidder request - * - */ - -/** - * @typedef {Object} PbBidderRequest - * @property {string} adUnitCode - Name of div or google adunit path - * @property {string} bidder - Bame of bidder - * @property {string} bidId - Identifies the bid request - * @property {Object} mediaTypes - * @property {Object} params - * @property {string} src - * @property {Object} userId - Map of userId module to module object - */ - -/** - * Tracks the bid request - * @param {PbBidRequest} bidRequest - */ -function onBidRequested(bidRequest) { - const {auctionId, bids: bidderRequests, start, timeout} = bidRequest; - const auction = auctionMap[auctionId]; - const adUnitCodeToAdUnitMap = auction.adUnitCodeToAdUnitMap; - - bidderRequests.forEach(bidderRequest => { - const { adUnitCode, bidder, bidId: requestId, mediaTypes, params, src, userId } = bidderRequest; - - auction.userIds.push(userId); - adUnitCodeToAdUnitMap[adUnitCode].bidRequestsMap[requestId] = { - bidder, - params, - mediaTypes, - source: src, - startTime: start, - timedOut: false, - timeLimit: timeout, - bids: {} - }; - }); -} - -/** - * - * @param {BidResponse} bidResponse - */ -function onBidResponse(bidResponse) { - let { - auctionId, - adUnitCode, - requestId, - cpm, - creativeId, - requestTimestamp, - responseTimestamp, - ts, - mediaType, - dealId, - ttl, - netRevenue, - currency, - originalCpm, - originalCurrency, - width, - height, - timeToRespond: latency, - adId, - meta - } = bidResponse; - - auctionMap[auctionId].adUnitCodeToAdUnitMap[adUnitCode].bidRequestsMap[requestId].bids[adId] = { - cpm, - creativeId, - requestTimestamp, - responseTimestamp, - ts, - adId, - meta, - mediaType, - dealId, - ttl, - netRevenue, - currency, - originalCpm, - originalCurrency, - width, - height, - latency, - winner: false, - rendered: false, - renderTime: 0, - }; -} - -function onBidTimeout(args) { - _each(args, ({auctionId, adUnitCode, bidId: requestId}) => { - let timedOutRequest = deepAccess(auctionMap, - `${auctionId}.adUnitCodeToAdUnitMap.${adUnitCode}.bidRequestsMap.${requestId}`); - - if (timedOutRequest) { - timedOutRequest.timedOut = true; - } - }); -} -/** - * - * @param {PbAuction} endedAuction - */ -function onAuctionEnd(endedAuction) { - let auction = auctionMap[endedAuction.auctionId]; - - if (!auction) { - return; - } - - clearAuctionTimer(auction); - auction.endTime = endedAuction.auctionEnd; - auction.state = AUCTION_STATES.ENDED; - delayedSend(auction); -} - -/** - * - * @param {BidResponse} bidResponse - */ -function onBidWon(bidResponse) { - const { auctionId, adUnitCode, requestId, adId } = bidResponse; - let winningBid = deepAccess(auctionMap, - `${auctionId}.adUnitCodeToAdUnitMap.${adUnitCode}.bidRequestsMap.${requestId}.bids.${adId}`); - - if (winningBid) { - winningBid.winner = true; - const auction = auctionMap[auctionId]; - if (auction.sent) { - const endpoint = (analyticsConfig.endpoint || ENDPOINT) + 'event'; - const bidder = auction.adUnitCodeToAdUnitMap[adUnitCode].bidRequestsMap[requestId].bidder; - ajax(`${endpoint}?t=win&b=${adId}&a=${analyticsConfig.orgId}&bidder=${bidder}&ts=${auction.startTime}`, - () => { - logInfo(`Openx Analytics - Sending complete impression event for ${adId} at ${Date.now()}`) - }); - } else { - logInfo(`Openx Analytics - impression event for ${adId} will be sent with auction data`) - } - } -} - -/** - * - * @param {GoogleTagSlot} slot - * @param {string} serviceName - */ -function onSlotLoadedV2({ slot }) { - const renderTime = Date.now(); - const elementId = slot.getSlotElementId(); - const bidId = slot.getTargeting('hb_adid')[0]; - - let [auction, adUnit, bid] = getPathToBidResponseByBidId(bidId); - - if (!auction) { - // attempt to get auction by adUnitCode - auction = getAuctionByGoogleTagSLot(slot); - - if (!auction) { - return; // slot is not participating in an active prebid auction - } - } - - clearAuctionTimer(auction); - - // track that an adunit code has completed within an auction - auction.adunitCodesRenderedCount++; - - // mark adunit as rendered - if (bid) { - let {x, y} = getPageOffset(); - bid.rendered = true; - bid.renderTime = renderTime; - adUnit.adPosition = isAtf(elementId, x, y) ? 'ATF' : 'BTF'; - } - - if (auction.adunitCodesRenderedCount === auction.adUnitCodesCount) { - auction.state = AUCTION_STATES.COMPLETED; - } - - // prepare to send regardless if auction is complete or not as a failsafe in case not all events are tracked - // add additional padding when not all slots are rendered - delayedSend(auction); -} - -function isAtf(elementId, scrollLeft = 0, scrollTop = 0) { - let elem = document.querySelector('#' + elementId); - let isAtf = false; - if (elem) { - let bounding = elem.getBoundingClientRect(); - if (bounding) { - let windowWidth = (window.innerWidth || document.documentElement.clientWidth); - let windowHeight = (window.innerHeight || document.documentElement.clientHeight); - - // intersection coordinates - let left = Math.max(0, bounding.left + scrollLeft); - let right = Math.min(windowWidth, bounding.right + scrollLeft); - let top = Math.max(0, bounding.top + scrollTop); - let bottom = Math.min(windowHeight, bounding.bottom + scrollTop); - - let intersectionWidth = right - left; - let intersectionHeight = bottom - top; - - let intersectionArea = (intersectionHeight > 0 && intersectionWidth > 0) ? (intersectionHeight * intersectionWidth) : 0; - let adSlotArea = (bounding.right - bounding.left) * (bounding.bottom - bounding.top); - - if (adSlotArea > 0) { - // Atleast 50% of intersection in window - isAtf = intersectionArea * 2 >= adSlotArea; - } - } - } else { - logWarn('OX: DOM element not for id ' + elementId); - } - return isAtf; -} - -// backwards compatible pageOffset from https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX -function getPageOffset() { - var x = (window.pageXOffset !== undefined) - ? window.pageXOffset - : (document.documentElement || document.body.parentNode || document.body).scrollLeft; - - var y = (window.pageYOffset !== undefined) - ? window.pageYOffset - : (document.documentElement || document.body.parentNode || document.body).scrollTop; - return {x, y}; -} - -function delayedSend(auction) { - if (auction.sent) { - return; - } - const delayTime = auction.adunitCodesRenderedCount === auction.adUnitCodesCount - ? analyticsConfig.payloadWaitTime - : analyticsConfig.payloadWaitTime + analyticsConfig.payloadWaitTimePadding; - - auction.auctionSendDelayTimer = setTimeout(() => { - auction.sent = true; // any BidWon emitted after this will be recorded separately - let payload = JSON.stringify([buildAuctionPayload(auction)]); - - ajax(analyticsConfig.endpoint || ENDPOINT, () => { - logInfo(`OpenX Analytics - Sending complete auction at ${Date.now()}`); - }, payload, { contentType: 'application/json' }); - }, delayTime); -} - -function clearAuctionTimer(auction) { - // reset the delay timer to send the auction data - if (auction.auctionSendDelayTimer) { - clearTimeout(auction.auctionSendDelayTimer); - auction.auctionSendDelayTimer = void (0); - } -} - -/** - * Returns the path to a bid (auction, adunit, bidRequest, and bid) based on a bidId - * @param {string} bidId - * @returns {Array<*>} - */ -function getPathToBidResponseByBidId(bidId) { - let auction; - let adUnit; - let bidResponse; - - if (!bidId) { - return []; - } - - _each(auctionMap, currentAuction => { - // skip completed auctions - if (currentAuction.state === AUCTION_STATES.COMPLETED) { - return; - } - - _each(currentAuction.adUnitCodeToAdUnitMap, (currentAdunit) => { - _each(currentAdunit.bidRequestsMap, currentBiddRequest => { - _each(currentBiddRequest.bids, (currentBidResponse, bidResponseId) => { - if (bidId === bidResponseId) { - auction = currentAuction; - adUnit = currentAdunit; - bidResponse = currentBidResponse; - } - }); - }); - }); - }); - return [auction, adUnit, bidResponse]; -} - -function getAuctionByGoogleTagSLot(slot) { - let slotAdunitCodes = [slot.getSlotElementId(), slot.getAdUnitPath()]; - let slotAuction; - - _each(auctionMap, auction => { - if (auction.state === AUCTION_STATES.COMPLETED) { - return; - } - - _each(auction.adUnitCodeToAdUnitMap, (bidderRequestIdMap, adUnitCode) => { - if (includes(slotAdunitCodes, adUnitCode)) { - slotAuction = auction; - } - }); - }); - - return slotAuction; -} - -function buildAuctionPayload(auction) { - let {startTime, endTime, state, timeout, auctionOrder, userIds, adUnitCodeToAdUnitMap, id} = auction; - const auctionId = id; - let {orgId, publisherPlatformId, publisherAccountId, campaign, testCode, configId, optimizerConfig} = analyticsConfig; - - return { - auctionId, - adapterVersion: ADAPTER_VERSION, - schemaVersion: SCHEMA_VERSION, - orgId, - publisherPlatformId, - publisherAccountId, - configId, - optimizerConfig, - campaign, - state, - startTime, - endTime, - timeLimit: timeout, - auctionOrder, - deviceType: detectMob() ? 'Mobile' : 'Desktop', - deviceOSType: detectOS(), - browser: detectBrowser(), - testCode: testCode, - // return an array of module name that have user data - userIdProviders: buildUserIdProviders(userIds), - adUnits: buildAdUnitsPayload(adUnitCodeToAdUnitMap), - }; - - function buildAdUnitsPayload(adUnitCodeToAdUnitMap) { - return _map(adUnitCodeToAdUnitMap, (adUnit) => { - let {code, adPosition} = adUnit; - - return { - code, - adPosition, - bidRequests: buildBidRequestPayload(adUnit.bidRequestsMap) - }; - - function buildBidRequestPayload(bidRequestsMap) { - return _map(bidRequestsMap, (bidRequest) => { - let {bidder, source, bids, mediaTypes, timeLimit, timedOut} = bidRequest; - return { - bidder, - source, - hasBidderResponded: Object.keys(bids).length > 0, - availableAdSizes: getMediaTypeSizes(mediaTypes), - availableMediaTypes: getMediaTypes(mediaTypes), - timeLimit, - timedOut, - bidResponses: _map(bidRequest.bids, (bidderBidResponse) => { - let { - adId, - cpm, - creativeId, - ts, - meta, - mediaType, - dealId, - ttl, - netRevenue, - currency, - width, - height, - latency, - winner, - rendered, - renderTime - } = bidderBidResponse; - - return { - bidId: adId, - microCpm: cpm * 1000000, - netRevenue, - currency, - mediaType, - height, - width, - size: `${width}x${height}`, - dealId, - latency, - ttl, - winner, - creativeId, - ts, - rendered, - renderTime, - meta - } - }) - } - }); - } - }); - } - - function buildUserIdProviders(userIds) { - return _map(userIds, (userId) => { - return _map(userId, (id, module) => { - return hasUserData(module, id) ? module : false - }).filter(module => module); - }).reduce(flatten, []).filter(uniques).sort(); - } - - function hasUserData(module, idOrIdObject) { - let normalizedId; - - switch (module) { - case 'digitrustid': - normalizedId = deepAccess(idOrIdObject, 'data.id'); - break; - case 'lipb': - normalizedId = idOrIdObject.lipbid; - break; - default: - normalizedId = idOrIdObject; - } - - return !isEmpty(normalizedId); - } - - function getMediaTypeSizes(mediaTypes) { - return _map(mediaTypes, (mediaTypeConfig, mediaType) => { - return parseSizesInput(mediaTypeConfig.sizes) - .map(size => `${mediaType}_${size}`); - }).reduce(flatten, []); - } - - function getMediaTypes(mediaTypes) { - return _map(mediaTypes, (mediaTypeConfig, mediaType) => mediaType); - } } diff --git a/modules/openxAnalyticsAdapter.md b/modules/openxAnalyticsAdapter.md index af40486f2a4..357e1520eb4 100644 --- a/modules/openxAnalyticsAdapter.md +++ b/modules/openxAnalyticsAdapter.md @@ -5,6 +5,10 @@ --- +# NOTE: OPENX NO LONGER OFFERS ANALYTICS +# THIS ADAPTER NO LONGER FUNCTIONS AND +# WILL BE REMOVED IN PREBID 8 + # About this Guide This implementation guide walks through the flow of onboarding an alpha Publisher to test OpenX’s new Analytics Adapter. diff --git a/test/spec/modules/openxAnalyticsAdapter_spec.js b/test/spec/modules/openxAnalyticsAdapter_spec.js index 47663a41f47..1b1adeb8807 100644 --- a/test/spec/modules/openxAnalyticsAdapter_spec.js +++ b/test/spec/modules/openxAnalyticsAdapter_spec.js @@ -1,653 +1,21 @@ import { expect } from 'chai'; -import openxAdapter, {AUCTION_STATES} from 'modules/openxAnalyticsAdapter.js'; -import * as events from 'src/events.js'; -import CONSTANTS from 'src/constants.json'; +import openxAdapter from 'modules/openxAnalyticsAdapter.js'; import * as utils from 'src/utils.js'; -import { server } from 'test/mocks/xhr.js'; -import {find} from 'src/polyfill.js'; - -const { - EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, BID_WON, AUCTION_END } -} = CONSTANTS; -const SLOT_LOADED = 'slotOnload'; -const CURRENT_TIME = 1586000000000; describe('openx analytics adapter', function() { - describe('when validating the configuration', function () { + describe('deprecation message', function () { let spy; beforeEach(function () { - spy = sinon.spy(utils, 'logError'); + spy = sinon.spy(utils, 'logWarn'); }); afterEach(function() { - utils.logError.restore(); + utils.logWarn.restore(); }); - it('should require organization id when no configuration is passed', function() { + it('should warn on enable', function() { openxAdapter.enableAnalytics(); - expect(spy.firstCall.args[0]).to.match(/publisherPlatformId/); - expect(spy.firstCall.args[0]).to.match(/to exist/); - }); - - it('should require publisher id when no orgId is passed', function() { - openxAdapter.enableAnalytics({ - provider: 'openx', - options: { - publisherAccountId: 12345 - } - }); - expect(spy.firstCall.args[0]).to.match(/publisherPlatformId/); - expect(spy.firstCall.args[0]).to.match(/to exist/); - }); - - it('should validate types', function() { - openxAdapter.enableAnalytics({ - provider: 'openx', - options: { - orgId: 'test platformId', - sampling: 'invalid-float' - } - }); - - expect(spy.firstCall.args[0]).to.match(/sampling/); - expect(spy.firstCall.args[0]).to.match(/type 'number'/); - }); - }); - - describe('when tracking analytic events', function () { - const AD_UNIT_CODE = 'test-div-1'; - const SLOT_LOAD_WAIT_TIME = 10; - - const DEFAULT_V2_ANALYTICS_CONFIG = { - orgId: 'test-org-id', - publisherAccountId: 123, - publisherPlatformId: 'test-platform-id', - configId: 'my_config', - optimizerConfig: 'my my optimizer', - sample: 1.0, - payloadWaitTime: SLOT_LOAD_WAIT_TIME, - payloadWaitTimePadding: SLOT_LOAD_WAIT_TIME - }; - - const auctionInit = { - auctionId: 'test-auction-id', - timestamp: CURRENT_TIME, - timeout: 3000, - adUnitCodes: [AD_UNIT_CODE], - }; - - const bidRequestedOpenX = { - auctionId: 'test-auction-id', - auctionStart: CURRENT_TIME, - timeout: 2000, - bids: [ - { - adUnitCode: AD_UNIT_CODE, - bidId: 'test-openx-request-id', - bidder: 'openx', - params: { unit: 'test-openx-ad-unit-id' }, - userId: { - tdid: 'test-tradedesk-id', - empty_id: '', - null_id: null, - bla_id: '', - digitrustid: { data: { id: '1' } }, - lipbid: { lipb: '2' } - } - } - ], - start: CURRENT_TIME + 10 - }; - - const bidRequestedCloseX = { - auctionId: 'test-auction-id', - auctionStart: CURRENT_TIME, - timeout: 1000, - bids: [ - { - adUnitCode: AD_UNIT_CODE, - bidId: 'test-closex-request-id', - bidder: 'closex', - params: { unit: 'test-closex-ad-unit-id' }, - userId: { - bla_id: '2', - tdid: 'test-tradedesk-id' - } - } - ], - start: CURRENT_TIME + 20 - }; - - const bidResponseOpenX = { - adUnitCode: AD_UNIT_CODE, - cpm: 0.5, - netRevenue: true, - requestId: 'test-openx-request-id', - mediaType: 'banner', - width: 300, - height: 250, - adId: 'test-openx-ad-id', - auctionId: 'test-auction-id', - creativeId: 'openx-crid', - currency: 'USD', - timeToRespond: 100, - responseTimestamp: CURRENT_TIME + 30, - ts: 'test-openx-ts' - }; - - const bidResponseCloseX = { - adUnitCode: AD_UNIT_CODE, - cpm: 0.3, - netRevenue: true, - requestId: 'test-closex-request-id', - mediaType: 'video', - width: 300, - height: 250, - adId: 'test-closex-ad-id', - auctionId: 'test-auction-id', - creativeId: 'closex-crid', - currency: 'USD', - timeToRespond: 200, - dealId: 'test-closex-deal-id', - responseTimestamp: CURRENT_TIME + 40, - ts: 'test-closex-ts' - }; - - const bidTimeoutOpenX = { - 0: { - adUnitCode: AD_UNIT_CODE, - auctionId: 'test-auction-id', - bidId: 'test-openx-request-id' - }}; - - const bidTimeoutCloseX = { - 0: { - adUnitCode: AD_UNIT_CODE, - auctionId: 'test-auction-id', - bidId: 'test-closex-request-id' - } - }; - - const bidWonOpenX = { - requestId: 'test-openx-request-id', - adId: 'test-openx-ad-id', - adUnitCode: AD_UNIT_CODE, - auctionId: 'test-auction-id' - }; - - const auctionEnd = { - auctionId: 'test-auction-id', - timestamp: CURRENT_TIME, - auctionEnd: CURRENT_TIME + 100, - timeout: 3000, - adUnitCodes: [AD_UNIT_CODE], - }; - - const bidWonCloseX = { - requestId: 'test-closex-request-id', - adId: 'test-closex-ad-id', - adUnitCode: AD_UNIT_CODE, - auctionId: 'test-auction-id' - }; - - function simulateAuction(events) { - let highestBid; - - events.forEach(event => { - const [eventType, args] = event; - if (eventType === BID_RESPONSE) { - highestBid = highestBid || args; - if (highestBid.cpm < args.cpm) { - highestBid = args; - } - } - - if (eventType === SLOT_LOADED) { - const slotLoaded = { - slot: { - getAdUnitPath: () => { - return '/12345678/test_ad_unit'; - }, - getSlotElementId: () => { - return AD_UNIT_CODE; - }, - getTargeting: (key) => { - if (key === 'hb_adid') { - return highestBid ? [highestBid.adId] : []; - } else { - return []; - } - } - } - }; - openxAdapter.track({ eventType, args: slotLoaded }); - } else { - openxAdapter.track({ eventType, args }); - } - }); - } - - let clock; - - beforeEach(function() { - sinon.stub(events, 'getEvents').returns([]); - clock = sinon.useFakeTimers(CURRENT_TIME); - }); - - afterEach(function() { - events.getEvents.restore(); - clock.restore(); - }); - - describe('when there is an auction', function () { - let auction; - let auction2; - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [SLOT_LOADED] - ]); - - simulateAuction([ - [AUCTION_INIT, {...auctionInit, auctionId: 'second-auction-id'}], - [SLOT_LOADED] - ]); - - clock.tick(SLOT_LOAD_WAIT_TIME); - auction = JSON.parse(server.requests[0].requestBody)[0]; - auction2 = JSON.parse(server.requests[1].requestBody)[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track auction start time', function () { - expect(auction.startTime).to.equal(auctionInit.timestamp); - }); - - it('should track auction time limit', function () { - expect(auction.timeLimit).to.equal(auctionInit.timeout); - }); - - it('should track the \'default\' test code', function () { - expect(auction.testCode).to.equal('default'); - }); - - it('should track auction count', function () { - expect(auction.auctionOrder).to.equal(1); - expect(auction2.auctionOrder).to.equal(2); - }); - - it('should track the orgId', function () { - expect(auction.orgId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.orgId); - }); - - it('should track the orgId', function () { - expect(auction.publisherPlatformId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.publisherPlatformId); - }); - - it('should track the orgId', function () { - expect(auction.publisherAccountId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.publisherAccountId); - }); - - it('should track the optimizerConfig', function () { - expect(auction.optimizerConfig).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.optimizerConfig); - }); - - it('should track the configId', function () { - expect(auction.configId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.configId); - }); - - it('should track the auction Id', function () { - expect(auction.auctionId).to.equal(auctionInit.auctionId); - }); - }); - - describe('when there is a custom test code', function () { - let auction; - beforeEach(function () { - openxAdapter.enableAnalytics({ - options: { - ...DEFAULT_V2_ANALYTICS_CONFIG, - testCode: 'test-code' - } - }); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [SLOT_LOADED], - ]); - clock.tick(SLOT_LOAD_WAIT_TIME); - auction = JSON.parse(server.requests[0].requestBody)[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track the custom test code', function () { - expect(auction.testCode).to.equal('test-code'); - }); - }); - - describe('when there is campaign (utm) data', function () { - let auction; - beforeEach(function () { - - }); - - afterEach(function () { - openxAdapter.reset(); - utils.getWindowLocation.restore(); - openxAdapter.disableAnalytics(); - }); - - it('should track values from query params when they exist', function () { - sinon.stub(utils, 'getWindowLocation').returns({search: '?' + - 'utm_campaign=test%20campaign-name&' + - 'utm_source=test-source&' + - 'utm_medium=test-medium&' - }); - - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [SLOT_LOADED], - ]); - clock.tick(SLOT_LOAD_WAIT_TIME); - auction = JSON.parse(server.requests[0].requestBody)[0]; - - // ensure that value are URI decoded - expect(auction.campaign.name).to.equal('test campaign-name'); - expect(auction.campaign.source).to.equal('test-source'); - expect(auction.campaign.medium).to.equal('test-medium'); - expect(auction.campaign.content).to.be.undefined; - expect(auction.campaign.term).to.be.undefined; - }); - - it('should override query params if configuration parameters exist', function () { - sinon.stub(utils, 'getWindowLocation').returns({search: '?' + - 'utm_campaign=test-campaign-name&' + - 'utm_source=test-source&' + - 'utm_medium=test-medium&' + - 'utm_content=test-content&' + - 'utm_term=test-term' - }); - - openxAdapter.enableAnalytics({ - options: { - ...DEFAULT_V2_ANALYTICS_CONFIG, - campaign: { - name: 'test-config-name', - source: 'test-config-source', - medium: 'test-config-medium' - } - } - }); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [SLOT_LOADED], - ]); - clock.tick(SLOT_LOAD_WAIT_TIME); - auction = JSON.parse(server.requests[0].requestBody)[0]; - - expect(auction.campaign.name).to.equal('test-config-name'); - expect(auction.campaign.source).to.equal('test-config-source'); - expect(auction.campaign.medium).to.equal('test-config-medium'); - expect(auction.campaign.content).to.equal('test-content'); - expect(auction.campaign.term).to.equal('test-term'); - }); - }); - - describe('when there are bid requests', function () { - let auction; - let openxBidder; - let closexBidder; - - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedCloseX], - [BID_REQUESTED, bidRequestedOpenX], - [SLOT_LOADED], - ]); - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - openxBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - closexBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex'); - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track the bidder', function () { - expect(openxBidder.bidder).to.equal('openx'); - expect(closexBidder.bidder).to.equal('closex'); - }); - - it('should track the adunit code', function () { - expect(auction.adUnits[0].code).to.equal(AD_UNIT_CODE); - }); - - it('should track the user ids', function () { - expect(auction.userIdProviders).to.deep.equal(['bla_id', 'digitrustid', 'lipbid', 'tdid']); - }); - - it('should not have responded', function () { - expect(openxBidder.hasBidderResponded).to.equal(false); - expect(closexBidder.hasBidderResponded).to.equal(false); - }); - }); - - describe('when there are request timeouts', function () { - let auction; - let openxBidRequest; - let closexBidRequest; - - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedCloseX], - [BID_REQUESTED, bidRequestedOpenX], - [BID_TIMEOUT, bidTimeoutCloseX], - [BID_TIMEOUT, bidTimeoutOpenX], - [AUCTION_END, auctionEnd] - ]); - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - - openxBidRequest = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - closexBidRequest = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex'); - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track the timeout', function () { - expect(openxBidRequest.timedOut).to.equal(true); - expect(closexBidRequest.timedOut).to.equal(true); - }); - - it('should track the timeout value ie timeLimit', function () { - expect(openxBidRequest.timeLimit).to.equal(2000); - expect(closexBidRequest.timeLimit).to.equal(1000); - }); - }); - - describe('when there are bid responses', function () { - let auction; - let openxBidResponse; - let closexBidResponse; - - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedCloseX], - [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX], - [BID_RESPONSE, bidResponseCloseX], - [AUCTION_END, auctionEnd] - ]); - - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - - openxBidResponse = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx').bidResponses[0]; - closexBidResponse = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex').bidResponses[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track the cpm in microCPM', function () { - expect(openxBidResponse.microCpm).to.equal(bidResponseOpenX.cpm * 1000000); - expect(closexBidResponse.microCpm).to.equal(bidResponseCloseX.cpm * 1000000); - }); - - it('should track if the bid is in net revenue', function () { - expect(openxBidResponse.netRevenue).to.equal(bidResponseOpenX.netRevenue); - expect(closexBidResponse.netRevenue).to.equal(bidResponseCloseX.netRevenue); - }); - - it('should track the mediaType', function () { - expect(openxBidResponse.mediaType).to.equal(bidResponseOpenX.mediaType); - expect(closexBidResponse.mediaType).to.equal(bidResponseCloseX.mediaType); - }); - - it('should track the currency', function () { - expect(openxBidResponse.currency).to.equal(bidResponseOpenX.currency); - expect(closexBidResponse.currency).to.equal(bidResponseCloseX.currency); - }); - - it('should track the ad width and height', function () { - expect(openxBidResponse.width).to.equal(bidResponseOpenX.width); - expect(openxBidResponse.height).to.equal(bidResponseOpenX.height); - - expect(closexBidResponse.width).to.equal(bidResponseCloseX.width); - expect(closexBidResponse.height).to.equal(bidResponseCloseX.height); - }); - - it('should track the bid dealId', function () { - expect(openxBidResponse.dealId).to.equal(bidResponseOpenX.dealId); // no deal id defined - expect(closexBidResponse.dealId).to.equal(bidResponseCloseX.dealId); // deal id defined - }); - - it('should track the bid\'s latency', function () { - expect(openxBidResponse.latency).to.equal(bidResponseOpenX.timeToRespond); - expect(closexBidResponse.latency).to.equal(bidResponseCloseX.timeToRespond); - }); - - it('should not have any bid winners', function () { - expect(openxBidResponse.winner).to.equal(false); - expect(closexBidResponse.winner).to.equal(false); - }); - - it('should track the bid currency', function () { - expect(openxBidResponse.currency).to.equal(bidResponseOpenX.currency); - expect(closexBidResponse.currency).to.equal(bidResponseCloseX.currency); - }); - - it('should track the auction end time', function () { - expect(auction.endTime).to.equal(auctionEnd.auctionEnd); - }); - - it('should track that the auction ended', function () { - expect(auction.state).to.equal(AUCTION_STATES.ENDED); - }); - }); - - describe('when there are bidder wins', function () { - let auction; - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_REQUESTED, bidRequestedCloseX], - [BID_RESPONSE, bidResponseOpenX], - [BID_RESPONSE, bidResponseCloseX], - [AUCTION_END, auctionEnd], - [BID_WON, bidWonOpenX] - ]); - - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track that bidder as the winner', function () { - let openxBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - expect(openxBidder.bidResponses[0]).to.contain({winner: true}); - }); - - it('should track that bidder as the losers', function () { - let closexBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex'); - expect(closexBidder.bidResponses[0]).to.contain({winner: false}); - }); - }); - - describe('when a winning bid renders', function () { - let auction; - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_REQUESTED, bidRequestedCloseX], - [BID_RESPONSE, bidResponseOpenX], - [BID_RESPONSE, bidResponseCloseX], - [AUCTION_END, auctionEnd], - [BID_WON, bidWonOpenX], - [SLOT_LOADED] - ]); - - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track that winning bid rendered', function () { - let openxBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - expect(openxBidder.bidResponses[0]).to.contain({rendered: true}); - }); - - it('should track that winning bid render time', function () { - let openxBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - expect(openxBidder.bidResponses[0]).to.contain({renderTime: CURRENT_TIME}); - }); - - it('should track that the auction completed', function () { - expect(auction.state).to.equal(AUCTION_STATES.COMPLETED); - }); + expect(spy.firstCall.args[0]).to.match(/OpenX Analytics has been deprecated/); }); }); }); From d836669df6529bf12f804c2d80cb0806084439e0 Mon Sep 17 00:00:00 2001 From: AndriiTokarGL <111281550+AndriiTokarGL@users.noreply.github.com> Date: Mon, 15 Aug 2022 16:57:43 +0300 Subject: [PATCH 141/569] Aniview Bid Adapter: add a new alias (#8832) --- modules/aniviewBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index 00280379133..84552638421 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -306,7 +306,7 @@ function getUserSyncs(syncOptions, serverResponses) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch', 'openwebvideo', 'didnavideo', 'ottadvisors'], + aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch', 'openwebvideo', 'didnavideo', 'ottadvisors', 'pgammedia'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid, buildRequests, From 9eda78a179ad4d5477fa9d5ebe8bd760fa925c95 Mon Sep 17 00:00:00 2001 From: pm-nitin-shirsat <107102698+pm-nitin-shirsat@users.noreply.github.com> Date: Mon, 15 Aug 2022 19:28:30 +0530 Subject: [PATCH 142/569] Pubmatic bid adapter: improved site object handling (#8820) * UOE-7836: Vanilla JS: Stop overwriting site.page site.ref and site.domain * UOE-7836: Vanilla JS: Stop overwriting site.page site.ref and site.domain in PrebidServerBidAdapter * Rename variable name * Revert changes from PrebidServerBidAdapter file * Test cases written for the ticket UOE-7666 - stop overriding page, domain, site --- modules/pubmaticBidAdapter.js | 6 ++- test/spec/modules/pubmaticBidAdapter_spec.js | 57 ++++++++++++++++++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 922fedd5092..b71fa5c8ed2 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -185,7 +185,7 @@ _each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] = anAss // loading NATIVE_ASSET_KEY_TO_ASSET_MAP _each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_KEY_TO_ASSET_MAP[anAsset.KEY] = anAsset }); -function _getDomainFromURL(url) { +export function _getDomainFromURL(url) { let anchor = document.createElement('a'); anchor.href = url; return anchor.hostname; @@ -1162,7 +1162,11 @@ export const spec = { // First Party Data const commonFpd = (bidderRequest && bidderRequest.ortb2) || {}; if (commonFpd.site) { + const { page, domain, ref } = payload.site; mergeDeep(payload, {site: commonFpd.site}); + payload.site.page = page; + payload.site.domain = domain; + payload.site.ref = ref; } if (commonFpd.user) { mergeDeep(payload, {user: commonFpd.user}); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index f3a49a6a925..688bec5b5e9 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec, checkVideoPlacement} from 'modules/pubmaticBidAdapter.js'; +import {spec, checkVideoPlacement, _getDomainFromURL} from 'modules/pubmaticBidAdapter.js'; import * as utils from 'src/utils.js'; import {config} from 'src/config.js'; import { createEidsArray } from 'modules/userId/eids.js'; @@ -1688,17 +1688,66 @@ describe('PubMatic adapter', function () { describe('FPD', function() { let newRequest; - it('ortb2.site should be merged in the request', function() { + describe('ortb2.site should not override page, domain & ref values', function() { + it('When above properties are present in ortb2.site', function() { + const ortb2 = { + site: { + domain: 'page.example.com', + page: 'https://page.example.com/here.html', + ref: 'https://page.example.com/here.html' + } + }; + const request = spec.buildRequests(bidRequests, {ortb2}); + let data = JSON.parse(request.data); + expect(data.site.domain).not.equal('page.example.com'); + expect(data.site.page).not.equal('https://page.example.com/here.html'); + expect(data.site.ref).not.equal('https://page.example.com/here.html'); + }); + + it('When above properties are absent in ortb2.site', function () { + const ortb2 = { + site: {} + }; + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id', + ortb2 + }); + let data = JSON.parse(request.data); + let response = spec.interpretResponse(bidResponses, request); + expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); + expect(data.site.domain).to.equal(_getDomainFromURL(data.site.page)); + expect(response[0].referrer).to.equal(data.site.ref); + }); + + it('With some extra properties in ortb2.site', function() { + const ortb2 = { + site: { + domain: 'page.example.com', + page: 'https://page.example.com/here.html', + ref: 'https://page.example.com/here.html', + cat: ['IAB2'], + sectioncat: ['IAB2-2'] + } + }; + const request = spec.buildRequests(bidRequests, {ortb2}); + let data = JSON.parse(request.data); + expect(data.site.domain).not.equal('page.example.com'); + expect(data.site.page).not.equal('https://page.example.com/here.html'); + expect(data.site.ref).not.equal('https://page.example.com/here.html'); + expect(data.site.cat).to.deep.equal(['IAB2']); + expect(data.site.sectioncat).to.deep.equal(['IAB2-2']); + }); + }); + + it('ortb2.site should be merged except page, domain & ref in the request', function() { const ortb2 = { site: { - domain: 'page.example.com', cat: ['IAB2'], sectioncat: ['IAB2-2'] } }; const request = spec.buildRequests(bidRequests, {ortb2}); let data = JSON.parse(request.data); - expect(data.site.domain).to.equal('page.example.com'); expect(data.site.cat).to.deep.equal(['IAB2']); expect(data.site.sectioncat).to.deep.equal(['IAB2-2']); }); From 902cfc087b85ee6162a7f49544f57f07ab26f06e Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 15 Aug 2022 07:00:39 -0700 Subject: [PATCH 143/569] Build system: fix "test-coverage" (#8819) --- babelConfig.js | 14 +- karma.conf.maker.js | 13 +- package-lock.json | 367 ++++++++++++++++++++++++++++++-------------- package.json | 4 +- 4 files changed, 265 insertions(+), 133 deletions(-) diff --git a/babelConfig.js b/babelConfig.js index 2592d8dc776..a88491c0cae 100644 --- a/babelConfig.js +++ b/babelConfig.js @@ -22,9 +22,15 @@ module.exports = function (options = {}) { } ] ], - 'plugins': [ - [path.resolve(__dirname, './plugins/pbjsGlobals.js'), options], - [useLocal('@babel/plugin-transform-runtime')], - ], + 'plugins': (() => { + const plugins = [ + [path.resolve(__dirname, './plugins/pbjsGlobals.js'), options], + [useLocal('@babel/plugin-transform-runtime')], + ]; + if (options.codeCoverage) { + plugins.push([useLocal('babel-plugin-istanbul')]) + } + return plugins; + })(), } } diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 82ede90b141..81930f42544 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -22,20 +22,9 @@ function newWebpackConfig(codeCoverage, disableFeatures) { .flatMap((r) => r.use) .filter((use) => use.loader === 'babel-loader') .forEach((use) => { - use.options = babelConfig({test: true, disableFeatures}); + use.options = babelConfig({test: true, codeCoverage, disableFeatures}); }); - if (codeCoverage) { - webpackConfig.module.rules.push({ - enforce: 'post', - exclude: /(node_modules)|(test)|(integrationExamples)|(build)|polyfill.js|(src\/adapters\/analytics\/ga.js)/, - use: { - loader: '@jsdevtools/coverage-istanbul-loader', - options: { esModules: true } - }, - test: /\.js$/ - }) - } return webpackConfig; } diff --git a/package-lock.json b/package-lock.json index 0e221a988fd..749fafaebc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "7.8.0-pre", + "version": "7.10.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -26,7 +26,6 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", - "@jsdevtools/coverage-istanbul-loader": "^3.0.3", "@wdio/browserstack-service": "^7.16.0", "@wdio/cli": "^7.5.2", "@wdio/concise-reporter": "^7.5.2", @@ -37,6 +36,7 @@ "ajv": "6.12.3", "assert": "^2.0.0", "babel-loader": "^8.0.5", + "babel-plugin-istanbul": "^6.1.1", "babel-register": "^6.26.0", "body-parser": "^1.19.0", "chai": "^4.2.0", @@ -1837,6 +1837,92 @@ "node": ">=6.9.0" } }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1978,19 +2064,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", - "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", - "dev": true, - "dependencies": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.3", - "loader-utils": "^2.0.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.7.0" - } - }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -4459,6 +4532,38 @@ "object.assign": "^4.1.0" } }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", @@ -10016,6 +10121,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/get-pkg-repo": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", @@ -14128,21 +14242,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -15405,20 +15504,6 @@ "node": ">=6.11.5" } }, - "node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, "node_modules/locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -16546,24 +16631,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/merge-source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -21427,6 +21494,20 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", @@ -24734,6 +24815,70 @@ "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", "dev": true }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -24844,19 +24989,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", - "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", - "dev": true, - "requires": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.3", - "loader-utils": "^2.0.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.7.0" - } - }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -26886,6 +27018,34 @@ "object.assign": "^4.1.0" } }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + } + } + }, "babel-plugin-polyfill-corejs2": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", @@ -31285,6 +31445,12 @@ "has-symbols": "^1.0.1" } }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, "get-pkg-repo": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", @@ -34570,18 +34736,6 @@ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, "istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -35534,17 +35688,6 @@ "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", "dev": true }, - "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -36476,23 +36619,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -40338,6 +40464,17 @@ } } }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, "text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", diff --git a/package.json b/package.json index 19748040d79..58898eb0110 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", - "@jsdevtools/coverage-istanbul-loader": "^3.0.3", "@wdio/browserstack-service": "^7.16.0", "@wdio/cli": "^7.5.2", "@wdio/concise-reporter": "^7.5.2", @@ -46,6 +45,7 @@ "ajv": "6.12.3", "assert": "^2.0.0", "babel-loader": "^8.0.5", + "babel-plugin-istanbul": "^6.1.1", "babel-register": "^6.26.0", "body-parser": "^1.19.0", "chai": "^4.2.0", @@ -114,9 +114,9 @@ }, "dependencies": { "@babel/core": "^7.16.7", + "@babel/plugin-transform-runtime": "^7.18.9", "@babel/preset-env": "^7.16.8", "@babel/runtime": "^7.18.9", - "@babel/plugin-transform-runtime": "^7.18.9", "core-js": "^3.13.0", "core-js-pure": "^3.13.0", "criteo-direct-rsa-validate": "^1.1.0", From 335a90782bc5f709a039b5b6a85811098e0b544f Mon Sep 17 00:00:00 2001 From: CPMStar Date: Mon, 15 Aug 2022 09:20:48 -0700 Subject: [PATCH 144/569] CPMStar Bid Adapter : change request method to post & added data to endpoint (#8810) * added cpmstarBidAdapter with meta.advertiserDomains support * fix linting * Updated modules/cpmstarBidAdapter: Changed request method to post and included the mediaType.video and adUnitCode data to the request body. Co-authored-by: Chris Huie Co-authored-by: = <=> --- modules/cpmstarBidAdapter.js | 32 ++++++++++++++------- test/spec/modules/cpmstarBidAdapter_spec.js | 2 +- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js index 6e32c5c4713..e8e1a846f35 100755 --- a/modules/cpmstarBidAdapter.js +++ b/modules/cpmstarBidAdapter.js @@ -1,4 +1,5 @@ -import { deepAccess, getBidIdParameter, logWarn, logError } from '../src/utils.js'; + +import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; @@ -25,11 +26,11 @@ export const spec = { getMediaType: function (bidRequest) { if (bidRequest == null) return BANNER; - return !deepAccess(bidRequest, 'mediaTypes.video') ? BANNER : VIDEO; + return !utils.deepAccess(bidRequest, 'mediaTypes.video') ? BANNER : VIDEO; }, getPlayerSize: function (bidRequest) { - var playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + var playerSize = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize'); if (playerSize == null) return [640, 440]; if (playerSize[0] != null) playerSize = playerSize[0]; if (playerSize == null || playerSize[0] == null || playerSize[1] == null) return [640, 440]; @@ -46,15 +47,14 @@ export const spec = { for (var i = 0; i < validBidRequests.length; i++) { var bidRequest = validBidRequests[i]; - // TODO: is 'page' the right value here? - var referer = encodeURIComponent(bidderRequest.refererInfo.page); - var e = getBidIdParameter('endpoint', bidRequest.params); + var referer = encodeURIComponent(bidderRequest.refererInfo.referer); + var e = utils.getBidIdParameter('endpoint', bidRequest.params); var ENDPOINT = e == 'dev' ? ENDPOINT_DEV : e == 'staging' ? ENDPOINT_STAGING : ENDPOINT_PRODUCTION; var mediaType = spec.getMediaType(bidRequest); var playerSize = spec.getPlayerSize(bidRequest); var videoArgs = '&fv=0' + (playerSize ? ('&w=' + playerSize[0] + '&h=' + playerSize[1]) : ''); var url = ENDPOINT + '?media=' + mediaType + (mediaType == VIDEO ? videoArgs : '') + - '&json=c_b&mv=1&poolid=' + getBidIdParameter('placementId', bidRequest.params) + + '&json=c_b&mv=1&poolid=' + utils.getBidIdParameter('placementId', bidRequest.params) + '&reachedTop=' + encodeURIComponent(bidderRequest.refererInfo.reachedTop) + '&requestid=' + bidRequest.bidId + '&referer=' + encodeURIComponent(referer); @@ -93,10 +93,20 @@ export const spec = { url += '&tfcd=' + (config.getConfig('coppa') ? 1 : 0); } + let body = {}; + let adUnitCode = bidRequest.adUnitCode; + if (adUnitCode) { + body.adUnitCode = adUnitCode; + } + if (mediaType == VIDEO) { + body.video = utils.deepAccess(bidRequest, 'mediaTypes.video'); + } + requests.push({ - method: 'GET', + method: 'POST', url: url, bidRequest: bidRequest, + data: body }); } @@ -117,13 +127,13 @@ export const spec = { var raw = serverResponse.body[i]; var rawBid = raw.creatives[0]; if (!rawBid) { - logWarn('cpmstarBidAdapter: server response failed check'); + utils.logWarn('cpmstarBidAdapter: server response failed check'); return; } var cpm = (parseFloat(rawBid.cpm) || 0); if (!cpm) { - logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') + utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') return; } @@ -156,7 +166,7 @@ export const spec = { bidResponse.mediaType = VIDEO; bidResponse.vastXml = rawBid.creativemacros.HTML5VID_VASTSTRING; } else { - return logError('bad response', rawBid); + return utils.logError('bad response', rawBid); } bidResponses.push(bidResponse); diff --git a/test/spec/modules/cpmstarBidAdapter_spec.js b/test/spec/modules/cpmstarBidAdapter_spec.js index dd076d060b9..285fca9690a 100755 --- a/test/spec/modules/cpmstarBidAdapter_spec.js +++ b/test/spec/modules/cpmstarBidAdapter_spec.js @@ -14,7 +14,7 @@ const valid_bid_requests = [{ const bidderRequest = { refererInfo: { - page: 'referer', + referer: 'referer', reachedTop: false, } }; From 6b0021c02c80b32c0e9b688064f8ff1790270057 Mon Sep 17 00:00:00 2001 From: Jeremy Sadwith Date: Mon, 15 Aug 2022 12:35:10 -0400 Subject: [PATCH 145/569] Kargo Bid Adapter: Pull Page URL from refererInfo (#8825) * Kargo Bid Adapter: Use currency from Bid Response * Kargo Bid Adapter: Fix failed test * Kargo Bid Adapter: adding media type to bid response, supporting vastXml response (#8426) * kargo adapter - adding mediaType to bid response, conditionally set vastXml field * kargo adapter - updating tests * Kargo Bid Adapter: onTimeout Support (#6) * Adding additional param * Adding response time function * Remove debug * Updating response time log to be set by bid response * Adding screen width/height to request * Test fix * Test fix * Removing interpretResponse signaling * Simplifying send data function * Kargo Analytics Adapter: Update with bid response time support * Renaming event route for auction data * Reset bid response data sent bool * Using array to store logged auctions * Update to use timeToResponse * Test fix * Kargo: Pull page URL from refererInfo * Test fix: Page URL * Removing extra bracket Co-authored-by: Wei Wong Co-authored-by: Andy Rusiecki --- modules/kargoBidAdapter.js | 9 ++++----- test/spec/modules/kargoBidAdapter_spec.js | 13 +++++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 72342903d87..deeb1cb8630 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -60,7 +60,7 @@ export const spec = { height: window.screen.height }, prebidRawBidRequests: validBidRequests - }, spec._getAllMetadata(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent)); + }, spec._getAllMetadata(bidderRequest, tdid)); const encodedParams = encodeURIComponent(JSON.stringify(transformedParams)); return Object.assign({}, bidderRequest, { method: 'GET', @@ -215,11 +215,10 @@ export const spec = { return crb.clientId; }, - _getAllMetadata(tdid, usp, gdpr) { + _getAllMetadata(bidderRequest, tdid) { return { - userIDs: spec._getUserIds(tdid, usp, gdpr), - // TODO: this should probably look at refererInfo - pageURL: window.location.href, + userIDs: spec._getUserIds(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent), + pageURL: bidderRequest.refererInfo && bidderRequest.refererInfo.page, rawCRB: storage.getCookie('krg_crb'), rawCRBLocalStorage: spec._getLocalStorageSafely('krg_crb') }; diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index e900b91ed11..169ee520f33 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -276,7 +276,7 @@ describe('kargo adapter tests', function () { optOut: false, usp: '1---' }, - pageURL: window.location.href, + pageURL: 'https://www.prebid.org', prebidRawBidRequests: [ { bidId: 1, @@ -327,10 +327,19 @@ describe('kargo adapter tests', function () { if (excludeTdid) { delete clonedBids[0].userId.tdid; } - var payload = { timeout: 200, uspConsent: '1---', foo: 'bar' }; + var payload = { + timeout: 200, + uspConsent: '1---', + foo: 'bar', + refererInfo: { + page: 'https://www.prebid.org', + }, + }; + if (gdpr) { payload['gdprConsent'] = gdpr } + var request = spec.buildRequests(clonedBids, payload); expected.sessionId = getSessionId(); sessionIds.push(expected.sessionId); From 6e36e363aa843603a8b7cb29da3242d84e5f4b9d Mon Sep 17 00:00:00 2001 From: caseywhitmire <60086994+caseywhitmire@users.noreply.github.com> Date: Mon, 15 Aug 2022 12:33:13 -0700 Subject: [PATCH 146/569] Various Files: Fix LGTM trailing semi-colon (#8830) * Update showheroes-bsBidAdapter.js * Update adyoulikeBidAdapter.js * Update sonobiBidAdapter.js * Update mediakeysBidAdapter.js * Update revcontentBidAdapter.js * Update vidazooBidAdapter.js * Update tpmnBidAdapter.js * Update yandexBidAdapter.js * Update yieldoneBidAdapter.js * Update karma.conf.maker.js * Update trustpidSystem.js * Update iqmBidAdapter.js * Update yieldmoBidAdapter.js * Update tappxBidAdapter.js * Update e_volutionBidAdapter.js * Update adrelevantisBidAdapter.js * Update goldbachBidAdapter.js * Update Renderer.js * Update priceFloors.js * Update bizzclickBidAdapter.js * Update nobidBidAdapter.js * Update openxBidAdapter.js * Update yahoosspBidAdapter.js * Update rtbhouseBidAdapter.js * Update admixerBidAdapter.js * Update gamoshiBidAdapter.js * Update pubxaiAnalyticsAdapter.js * Update impactifyBidAdapter.js * Update pubwiseAnalyticsAdapter.js * Update sizeMappingV2.js * Update pubwiseBidAdapter.js * Update theAdxBidAdapter.js * Update staqAnalyticsAdapter.js * Update adtelligentBidAdapter.js * Update novatiqIdSystem.js * Update cwireBidAdapter.js * Update datablocksBidAdapter.js * Update gothamadsBidAdapter.js * Update krushmediaBidAdapter.js * Update vidoomyBidAdapter.js * Update adtrueBidAdapter.js * Update userSync.js * Update targetVideoBidAdapter.js * Update open8BidAdapter.js * Update gumgumBidAdapter.js * Update mgidBidAdapter.js * Update inskinBidAdapter.js * Update adxpremiumAnalyticsAdapter.js * Update emx_digitalBidAdapter.js * Update betweenBidAdapter.js * Update pixfutureBidAdapter.js * Update winrBidAdapter.js * Update luponmediaBidAdapter.js * Update interactiveOffersBidAdapter.js * Update intentIqIdSystem.js * Update lkqdBidAdapter.js * Update loganBidAdapter.js * Update dfpAdServerVideo.js * Update targeting.js * Update livewrappedAnalyticsAdapter.js * Update adnowBidAdapter.js * Update cleanmedianetBidAdapter.js * Update sonobiAnalyticsAdapter.js * Update apstreamBidAdapter.js * Update quantcastIdSystem.js * Update prebidmanagerAnalyticsAdapter.js * Update brightMountainMediaBidAdapter.js * Update dgkeywordRtdProvider.js * Update h12mediaBidAdapter.js * Update permutiveRtdProvider_example.html * Update dspxBidAdapter.js * Update radsBidAdapter.js * Update id5AnalyticsAdapter.js * Update richaudienceBidAdapter.js * Update cpmstarBidAdapter.js * Update vdoaiBidAdapter.js * Update smaatoBidAdapter.js * Update adtargetBidAdapter.js * Update openwebBidAdapter.js * Update malltvBidAdapter.js * Update gjirafaBidAdapter.js * Update integr8BidAdapter.js * Update fabrickIdSystem.js * Update sizeMapping.js * Update adpod.js * Update adriverBidAdapter.js * Update trionBidAdapter.js * Update ebdrBidAdapter.js * Update getintentBidAdapter.js * Update pubxBidAdapter.js * Update schain.js * Update underdogmediaBidAdapter.js * Update bidfactory.js * eslint: update semi-colon rule Missing semi-colons will now cause an error. * Update .eslintrc.js --- .../gpt/permutiveRtdProvider_example.html | 2 +- karma.conf.maker.js | 2 +- modules/admixerBidAdapter.js | 2 +- modules/adnowBidAdapter.js | 2 +- modules/adpod.js | 2 +- modules/adrelevantisBidAdapter.js | 4 ++-- modules/adriverBidAdapter.js | 2 +- modules/adtargetBidAdapter.js | 2 +- modules/adtelligentBidAdapter.js | 2 +- modules/adtrueBidAdapter.js | 2 +- modules/adxpremiumAnalyticsAdapter.js | 2 +- modules/adyoulikeBidAdapter.js | 2 +- modules/apstreamBidAdapter.js | 4 ++-- modules/betweenBidAdapter.js | 2 +- modules/bizzclickBidAdapter.js | 2 +- modules/brightMountainMediaBidAdapter.js | 2 +- modules/cleanmedianetBidAdapter.js | 2 +- modules/cpmstarBidAdapter.js | 6 +++--- modules/cwireBidAdapter.js | 2 +- modules/datablocksBidAdapter.js | 2 +- modules/dfpAdServerVideo.js | 4 ++-- modules/dgkeywordRtdProvider.js | 2 +- modules/dspxBidAdapter.js | 2 +- modules/e_volutionBidAdapter.js | 6 +++--- modules/ebdrBidAdapter.js | 2 +- modules/emx_digitalBidAdapter.js | 2 +- modules/fabrickIdSystem.js | 4 ++-- modules/gamoshiBidAdapter.js | 2 +- modules/getintentBidAdapter.js | 2 +- modules/gjirafaBidAdapter.js | 4 ++-- modules/goldbachBidAdapter.js | 16 ++++++++-------- modules/gothamadsBidAdapter.js | 4 ++-- modules/gumgumBidAdapter.js | 6 +++--- modules/h12mediaBidAdapter.js | 2 +- modules/id5AnalyticsAdapter.js | 2 +- modules/impactifyBidAdapter.js | 2 +- modules/inskinBidAdapter.js | 2 +- modules/integr8BidAdapter.js | 4 ++-- modules/intentIqIdSystem.js | 4 ++-- modules/interactiveOffersBidAdapter.js | 2 +- modules/iqmBidAdapter.js | 2 +- modules/krushmediaBidAdapter.js | 4 ++-- modules/livewrappedAnalyticsAdapter.js | 6 +++--- modules/lkqdBidAdapter.js | 6 +++--- modules/loganBidAdapter.js | 4 ++-- modules/luponmediaBidAdapter.js | 6 +++--- modules/malltvBidAdapter.js | 4 ++-- modules/mediakeysBidAdapter.js | 4 ++-- modules/mgidBidAdapter.js | 4 ++-- modules/nobidBidAdapter.js | 4 ++-- modules/novatiqIdSystem.js | 2 +- modules/open8BidAdapter.js | 2 +- modules/openwebBidAdapter.js | 2 +- modules/openxBidAdapter.js | 6 +++--- modules/pixfutureBidAdapter.js | 2 +- modules/prebidmanagerAnalyticsAdapter.js | 2 +- modules/priceFloors.js | 2 +- modules/pubwiseAnalyticsAdapter.js | 2 +- modules/pubwiseBidAdapter.js | 6 +++--- modules/pubxBidAdapter.js | 2 +- modules/pubxaiAnalyticsAdapter.js | 4 ++-- modules/quantcastIdSystem.js | 2 +- modules/radsBidAdapter.js | 2 +- modules/revcontentBidAdapter.js | 2 +- modules/richaudienceBidAdapter.js | 2 +- modules/rtbhouseBidAdapter.js | 2 +- modules/schain.js | 2 +- modules/showheroes-bsBidAdapter.js | 2 +- modules/sizeMappingV2.js | 2 +- modules/smaatoBidAdapter.js | 4 ++-- modules/sonobiAnalyticsAdapter.js | 2 +- modules/sonobiBidAdapter.js | 6 +++--- modules/staqAnalyticsAdapter.js | 2 +- modules/tappxBidAdapter.js | 4 ++-- modules/targetVideoBidAdapter.js | 2 +- modules/theAdxBidAdapter.js | 4 ++-- modules/tpmnBidAdapter.js | 2 +- modules/trionBidAdapter.js | 4 ++-- modules/trustpidSystem.js | 2 +- modules/underdogmediaBidAdapter.js | 2 +- modules/vdoaiBidAdapter.js | 2 +- modules/vidazooBidAdapter.js | 2 +- modules/vidoomyBidAdapter.js | 2 +- modules/winrBidAdapter.js | 2 +- modules/yahoosspBidAdapter.js | 4 ++-- modules/yandexBidAdapter.js | 2 +- modules/yieldmoBidAdapter.js | 6 +++--- modules/yieldoneBidAdapter.js | 4 ++-- src/Renderer.js | 2 +- src/bidfactory.js | 2 +- src/sizeMapping.js | 2 +- src/targeting.js | 2 +- src/userSync.js | 2 +- 93 files changed, 141 insertions(+), 141 deletions(-) diff --git a/integrationExamples/gpt/permutiveRtdProvider_example.html b/integrationExamples/gpt/permutiveRtdProvider_example.html index dbb4d2af0d6..118cc678726 100644 --- a/integrationExamples/gpt/permutiveRtdProvider_example.html +++ b/integrationExamples/gpt/permutiveRtdProvider_example.html @@ -23,7 +23,7 @@ } } - setLocalStorageData() + setLocalStorageData(); var div_1_sizes = [ [300, 250], diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 81930f42544..fd8f2e6fdd8 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -176,7 +176,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file, disableFe concurrency: 6, plugins: plugins - } + }; // To ensure that, we are able to run single spec file // here we are adding preprocessors, when file is passed diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index b9cde172ebe..8fcee60f9f9 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -52,7 +52,7 @@ export const spec = { consentString: bidderRequest.gdprConsent.consentString, // will check if the gdprApplies field was populated with a boolean value (ie from page config). If it's undefined, then default to true gdprApplies: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true - } + }; } if (bidderRequest.uspConsent) { payload.uspConsent = bidderRequest.uspConsent; diff --git a/modules/adnowBidAdapter.js b/modules/adnowBidAdapter.js index 484b6202479..f83dbf68a1f 100644 --- a/modules/adnowBidAdapter.js +++ b/modules/adnowBidAdapter.js @@ -67,7 +67,7 @@ export const spec = { if (mediaType === BANNER) { data.sizes = parseSizesInput( req.mediaTypes && req.mediaTypes.banner && req.mediaTypes.banner.sizes - ).join('|') + ).join('|'); } else { data.width = data.height = 200; diff --git a/modules/adpod.js b/modules/adpod.js index b7c459fd66f..f1ab4bd2ef1 100644 --- a/modules/adpod.js +++ b/modules/adpod.js @@ -243,7 +243,7 @@ export function callPrebidCacheHook(fn, auctionInstance, bidResponse, afterBidAd let brandCategoryExclusion = config.getConfig('adpod.brandCategoryExclusion'); let adServerCatId = deepAccess(bidResponse, 'meta.adServerCatId'); if (!adServerCatId && brandCategoryExclusion) { - logWarn('Detected a bid without meta.adServerCatId while setConfig({adpod.brandCategoryExclusion}) was enabled. This bid has been rejected:', bidResponse) + logWarn('Detected a bid without meta.adServerCatId while setConfig({adpod.brandCategoryExclusion}) was enabled. This bid has been rejected:', bidResponse); afterBidAdded(); } else { if (config.getConfig('adpod.deferCaching') === false) { diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 193c92cbb19..40cfe18f025 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -209,7 +209,7 @@ export const spec = { return params; } -} +}; function isPopulatedArray(arr) { return !!(isArray(arr) && arr.length > 0); @@ -444,7 +444,7 @@ function bidToTag(bid) { tag.cpm = bid.params.cpm; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; if (bid.params.position) { diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js index 5ab417520e9..e95f83d2c7b 100644 --- a/modules/adriverBidAdapter.js +++ b/modules/adriverBidAdapter.js @@ -32,7 +32,7 @@ export const spec = { let timeout = null; if (bidderRequest) { - timeout = bidderRequest.timeout + timeout = bidderRequest.timeout; } const payload = { diff --git a/modules/adtargetBidAdapter.js b/modules/adtargetBidAdapter.js index c4f2bc65655..89ba4878acf 100644 --- a/modules/adtargetBidAdapter.js +++ b/modules/adtargetBidAdapter.js @@ -137,7 +137,7 @@ function bidToTag(bidRequests, adapterRequest) { tag.UserIds = deepAccess(bidRequests[0], 'userId'); } - const bids = [] + const bids = []; for (let i = 0, length = bidRequests.length; i < length; i++) { const bid = prepareBidRequests(bidRequests[i]); diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 03d4e634062..2ee5b0f72a3 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -190,7 +190,7 @@ function bidToTag(bidRequests, adapterRequest) { } // end publisher env - const bids = [] + const bids = []; for (let i = 0, length = bidRequests.length; i < length; i++) { const bid = prepareBidRequests(bidRequests[i]); diff --git a/modules/adtrueBidAdapter.js b/modules/adtrueBidAdapter.js index b0aafc3ae4e..2ec5cd59e1d 100644 --- a/modules/adtrueBidAdapter.js +++ b/modules/adtrueBidAdapter.js @@ -45,7 +45,7 @@ const VIDEO_CUSTOM_PARAMS = { 'placement': DATA_TYPES.NUMBER, 'minbitrate': DATA_TYPES.NUMBER, 'maxbitrate': DATA_TYPES.NUMBER -} +}; const NATIVE_ASSETS = { 'TITLE': {ID: 1, KEY: 'title', TYPE: 0}, diff --git a/modules/adxpremiumAnalyticsAdapter.js b/modules/adxpremiumAnalyticsAdapter.js index 456180a964b..9161c6338f4 100644 --- a/modules/adxpremiumAnalyticsAdapter.js +++ b/modules/adxpremiumAnalyticsAdapter.js @@ -263,7 +263,7 @@ adxpremiumAnalyticsAdapter.enableAnalytics = function (config) { } adxpremiumAnalyticsAdapter.originEnableAnalytics(config); // call the base class function -} +}; adapterManager.registerAnalyticsAdapter({ adapter: adxpremiumAnalyticsAdapter, diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 0f373749a7f..b0b132abd1c 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -444,7 +444,7 @@ function getNativeAssets(response, nativeConfig) { /* Create bid from response */ function createBid(response, bidRequests) { if (!response || (!response.Ad && !response.Native && !response.Vast)) { - return + return; } const request = bidRequests && bidRequests[response.BidID]; diff --git a/modules/apstreamBidAdapter.js b/modules/apstreamBidAdapter.js index 5ae0d5c56fd..30abb17bfde 100644 --- a/modules/apstreamBidAdapter.js +++ b/modules/apstreamBidAdapter.js @@ -267,7 +267,7 @@ var dsuModule = (function() { return { readOrCreateDsu: readOrCreateDsu - } + }; })(); function serializeSizes(sizes) { @@ -343,7 +343,7 @@ function getBids(bids) { const bidId = bid.bidId; let mediaType = ''; - const mediaTypes = Object.keys(bid.mediaTypes) + const mediaTypes = Object.keys(bid.mediaTypes); switch (mediaTypes[0]) { case 'video': mediaType = 'v'; diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 9e57d0f5cd3..2ca829b796b 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -90,7 +90,7 @@ export const spec = { } } - requests.push({data: params}) + requests.push({data: params}); }) return { method: 'POST', diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js index 7b32ffd84b0..4ef2b6dd9f8 100644 --- a/modules/bizzclickBidAdapter.js +++ b/modules/bizzclickBidAdapter.js @@ -256,7 +256,7 @@ const addNativeParameters = bidRequest => { wmin = sizes[0]; hmin = sizes[1]; } - asset[props.name] = {} + asset[props.name] = {}; if (bidParams.len) asset[props.name]['len'] = bidParams.len; if (props.type) asset[props.name]['type'] = props.type; if (wmin) asset[props.name]['wmin'] = wmin; diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index bfe1e8ecb29..6db06744c24 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -99,7 +99,7 @@ export const spec = { let response; try { - response = serverResponse.body + response = serverResponse.body; bid = response.seatbid[0].bid[0]; } catch (e) { response = null; diff --git a/modules/cleanmedianetBidAdapter.js b/modules/cleanmedianetBidAdapter.js index f7d74c0df64..ba72eec3d95 100644 --- a/modules/cleanmedianetBidAdapter.js +++ b/modules/cleanmedianetBidAdapter.js @@ -104,7 +104,7 @@ export const spec = { ext: { consent: bidderRequest.gdprConsent.consentString } - } + }; } const imp = { diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js index e8e1a846f35..64fe85fdbbc 100755 --- a/modules/cpmstarBidAdapter.js +++ b/modules/cpmstarBidAdapter.js @@ -73,7 +73,7 @@ export const spec = { fixedEncodeURIComponent(node.name || '') + ',' + fixedEncodeURIComponent(node.domain || ''); } - url += '&schain=' + schainString + url += '&schain=' + schainString; } if (bidderRequest.gdprConsent) { @@ -133,7 +133,7 @@ export const spec = { var cpm = (parseFloat(rawBid.cpm) || 0); if (!cpm) { - utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') + utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm'); return; } @@ -152,7 +152,7 @@ export const spec = { }; if (rawBid.hasOwnProperty('dealId')) { - bidResponse.dealId = rawBid.dealId + bidResponse.dealId = rawBid.dealId; } if (mediaType == BANNER && rawBid.code) { diff --git a/modules/cwireBidAdapter.js b/modules/cwireBidAdapter.js index 47f3020fec3..a11899609bc 100644 --- a/modules/cwireBidAdapter.js +++ b/modules/cwireBidAdapter.js @@ -303,5 +303,5 @@ export const spec = { return bidResponses; }, -} +}; registerBidder(spec); diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index f0e0c32990a..4e21e08ba57 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -361,7 +361,7 @@ export const spec = { stack: bidderRequest.refererInfo.stack, timeout: config.getConfig('bidderTimeout') }, - } + }; // ADD REF URL IF FOUND if (self === top && document.referrer) { diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 37f038d2a67..072715b4bd6 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -91,7 +91,7 @@ export function buildDfpVideoUrl(options) { }; const urlSearchComponent = urlComponents.search; - const urlSzParam = urlSearchComponent && urlSearchComponent.sz + const urlSzParam = urlSearchComponent && urlSearchComponent.sz; if (urlSzParam) { derivedParams.sz = urlSzParam + '|' + derivedParams.sz; } @@ -186,7 +186,7 @@ export function buildAdpodVideoUrl({code, params, callback} = {}) { let initialValue = { [adpodUtils.TARGETING_KEY_PB_CAT_DUR]: undefined, [adpodUtils.TARGETING_KEY_CACHE_ID]: undefined - } + }; let customParams = {}; if (targeting[code]) { customParams = targeting[code].reduce((acc, curValue) => { diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js index 171766ac4ce..cea33014144 100644 --- a/modules/dgkeywordRtdProvider.js +++ b/modules/dgkeywordRtdProvider.js @@ -48,7 +48,7 @@ export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, us keywords['opectx'] = res['t']; } if (Object.keys(keywords).length > 0) { - const targetBidKeys = {} + const targetBidKeys = {}; for (let bid of setKeywordTargetBidders) { // set keywords to params bid.params.keywords = keywords; diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index 6af0236d3bb..8850eb282b5 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -99,7 +99,7 @@ export const spec = { method: 'GET', url: endpoint, data: objectToQueryString(payload), - } + }; }); }, interpretResponse: function(serverResponse, bidRequest) { diff --git a/modules/e_volutionBidAdapter.js b/modules/e_volutionBidAdapter.js index 8e829a39a57..5f1b46ff9eb 100644 --- a/modules/e_volutionBidAdapter.js +++ b/modules/e_volutionBidAdapter.js @@ -72,7 +72,7 @@ export const spec = { let location; // TODO: this odd try-catch block was copied in several adapters; it doesn't seem to be correct for cross-origin try { - location = new URL(bidderRequest.refererInfo.page) + location = new URL(bidderRequest.refererInfo.page); winTop = window.top; } catch (e) { location = winTop.location; @@ -93,7 +93,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } const len = validBidRequests.length; @@ -106,7 +106,7 @@ export const spec = { bidId: bid.bidId, bidfloor: getBidFloor(bid), eids: [] - } + }; if (bid.userId) { getUserId(placement.eids, bid.userId.id5id, 'id5-sync.com'); diff --git a/modules/ebdrBidAdapter.js b/modules/ebdrBidAdapter.js index 62a3b171b74..a7b1991df9b 100644 --- a/modules/ebdrBidAdapter.js +++ b/modules/ebdrBidAdapter.js @@ -31,7 +31,7 @@ export const spec = { h: whArr[1] }, bidfloor: bidFloor - }) + }); ebdrReq[bid.bidId] = {mediaTypes: _mediaTypes, w: whArr[0], h: whArr[1] diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index d904ebda9c8..eb69e76a837 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -287,7 +287,7 @@ export const spec = { emxData = emxAdapter.getGdpr(bidderRequest, Object.assign({}, emxData)); emxData = emxAdapter.getSupplyChain(bidderRequest, Object.assign({}, emxData)); if (bidderRequest && bidderRequest.uspConsent) { - emxData.us_privacy = bidderRequest.uspConsent + emxData.us_privacy = bidderRequest.uspConsent; } // adding eid support diff --git a/modules/fabrickIdSystem.js b/modules/fabrickIdSystem.js index 24eac8517b0..dfc2985b0db 100644 --- a/modules/fabrickIdSystem.js +++ b/modules/fabrickIdSystem.js @@ -70,7 +70,7 @@ export const fabrickIdSubmodule = { } } // pull off the trailing & - url = url.slice(0, -1) + url = url.slice(0, -1); const referer = _getRefererInfo(configParams); const refs = new Map(); _setReferrer(refs, referer.topmostLocation); @@ -174,7 +174,7 @@ export function appendUrl(url, paramName, s, configParams) { s = s.substring(0, thisMaxRefLen - 2); } } - return `${url}${s}` + return `${url}${s}`; } else { return url; } diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index 26afe6e6b87..ae320db1251 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -259,7 +259,7 @@ export const spec = { let gdpr = gdprApplies ? 1 : 0; if (gdprApplies && gdprConsent.consentString) { - consentString = encodeURIComponent(gdprConsent.consentString) + consentString = encodeURIComponent(gdprConsent.consentString); } if (uspConsent) { diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js index 98659cc25e2..25322d81f9b 100644 --- a/modules/getintentBidAdapter.js +++ b/modules/getintentBidAdapter.js @@ -97,7 +97,7 @@ export const spec = { return bids; } -} +}; function buildUrl(bid) { return 'https://' + BID_HOST + (bid.is_video ? BID_VIDEO_PATH : BID_BANNER_PATH); diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index af70c0c67f0..91ed5c9b3fb 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -74,7 +74,7 @@ export const spec = { placements: placements, contents: contents, data: data - } + }; return [{ method: 'POST', @@ -113,7 +113,7 @@ export const spec = { } return bidResponses; } -} +}; /** * Generate size param for bid request using sizes array diff --git a/modules/goldbachBidAdapter.js b/modules/goldbachBidAdapter.js index a6e5c59ced2..21c56643353 100644 --- a/modules/goldbachBidAdapter.js +++ b/modules/goldbachBidAdapter.js @@ -207,7 +207,7 @@ export const spec = { payload['iab_support'] = { omidpn: 'Appnexus', omidpv: '$prebid.version$' - } + }; } if (member > 0) { @@ -215,7 +215,7 @@ export const spec = { } if (appDeviceObjBid) { - payload.device = appDeviceObj + payload.device = appDeviceObj; } if (appIdObjBid) { payload.app = appIdObj; @@ -246,7 +246,7 @@ export const spec = { } if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent + payload.us_privacy = bidderRequest.uspConsent; } if (bidderRequest && bidderRequest.refererInfo) { @@ -256,7 +256,7 @@ export const spec = { rd_top: bidderRequest.refererInfo.reachedTop, rd_ifs: bidderRequest.refererInfo.numIframes, rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - } + }; payload.referrer_detection = refererinfo; } @@ -441,7 +441,7 @@ export const spec = { reloadViewabilityScriptWithCorrectParameters(bid); } } -} +}; function isPopulatedArray(arr) { return !!(isArray(arr) && arr.length > 0); @@ -459,7 +459,7 @@ function reloadViewabilityScriptWithCorrectParameters(bid) { if (viewJsPayload) { let prebidParams = 'pbjs_adid=' + bid.adId + ';pbjs_auc=' + bid.adUnitCode; - let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload) + let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload); let newJsTrackerSrc = jsTrackerSrc.replace('dom_id=%native_dom_id%', prebidParams); @@ -553,7 +553,7 @@ function formatRequest(payload, bidderRequest) { if (getParameterByName('apn_test').toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { options.customHeaders = { 'X-Is-Test': 1 - } + }; } if (payload.tags.length > MAX_IMPS_PER_REQUEST) { @@ -766,7 +766,7 @@ function bidToTag(bid) { tag.code = bid.params.invCode; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; let bidFloor = getBidFloor(bid); diff --git a/modules/gothamadsBidAdapter.js b/modules/gothamadsBidAdapter.js index 37433e547a9..6b549f347bb 100644 --- a/modules/gothamadsBidAdapter.js +++ b/modules/gothamadsBidAdapter.js @@ -139,7 +139,7 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: (serverResponse) => { - if (!serverResponse || !serverResponse.body) return [] + if (!serverResponse || !serverResponse.body) return []; let GothamAdsResponse = serverResponse.body; let bids = []; @@ -273,7 +273,7 @@ const addNativeParameters = bidRequest => { hmin = sizes[1]; } - asset[props.name] = {} + asset[props.name] = {}; if (bidParams.len) asset[props.name]['len'] = bidParams.len; if (props.type) asset[props.name]['type'] = props.type; diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index b77f82030e1..fe15602c0c0 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -321,7 +321,7 @@ function buildRequests(validBidRequests, bidderRequest) { data.to = to; // ADTS-169 add adUnitCode to requests - if (adUnitCode) data.aun = adUnitCode + if (adUnitCode) data.aun = adUnitCode; // ADTS-134 Retrieve ID envelopes for (const eid in eids) data[eid] = eids[eid]; @@ -374,7 +374,7 @@ function buildRequests(validBidRequests, bidderRequest) { data.pi = 8; } } else { // legacy params - data = { ...data, ...handleLegacyParams(params, sizes) } + data = { ...data, ...handleLegacyParams(params, sizes) }; } if (gdprConsent) { @@ -400,7 +400,7 @@ function buildRequests(validBidRequests, bidderRequest) { url: BID_ENDPOINT, method: 'GET', data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId)) - }) + }); }); return bids; } diff --git a/modules/h12mediaBidAdapter.js b/modules/h12mediaBidAdapter.js index 29d8bfa5e0f..c58f15c9a81 100644 --- a/modules/h12mediaBidAdapter.js +++ b/modules/h12mediaBidAdapter.js @@ -198,7 +198,7 @@ function getIsHidden(elem) { } catch (o) { return false; } - } while ((m < 250) && (lastElem != null) && (elemHidden === false)) + } while ((m < 250) && (lastElem != null) && (elemHidden === false)); return elemHidden; } diff --git a/modules/id5AnalyticsAdapter.js b/modules/id5AnalyticsAdapter.js index 5ae04eb57ce..d35cdf29fca 100644 --- a/modules/id5AnalyticsAdapter.js +++ b/modules/id5AnalyticsAdapter.js @@ -141,7 +141,7 @@ const ENABLE_FUNCTION = (config) => { logInfo('id5Analytics: Tracking events', _this.eventsToTrack); if (sampling > 0 && _this.random() < (1 / sampling)) { // Init the module only if we got lucky - logInfo('id5Analytics: Selected by sampling. Starting up!') + logInfo('id5Analytics: Selected by sampling. Starting up!'); // Clean start _this.eventBuffer = {}; diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index bb3ad63085c..0717cf43741 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -25,7 +25,7 @@ const getDeviceType = () => { return 4; } return 2; -} +}; const createOpenRtbRequest = (validBidRequests, bidderRequest) => { // Create request and set imp bids inside diff --git a/modules/inskinBidAdapter.js b/modules/inskinBidAdapter.js index b76a759a047..f3464765bde 100644 --- a/modules/inskinBidAdapter.js +++ b/modules/inskinBidAdapter.js @@ -184,7 +184,7 @@ export const spec = { bid.currency = 'USD'; bid.creativeId = decision.adId; bid.ttl = 360; - bid.meta = { advertiserDomains: decision.adomain ? decision.adomain : [] } + bid.meta = { advertiserDomains: decision.adomain ? decision.adomain : [] }; bid.netRevenue = true; bidResponses.push(bid); diff --git a/modules/integr8BidAdapter.js b/modules/integr8BidAdapter.js index 3ba68ffb6d6..a85e9b0a55c 100644 --- a/modules/integr8BidAdapter.js +++ b/modules/integr8BidAdapter.js @@ -78,7 +78,7 @@ export const spec = { placements: placements, contents: contents, data: data - } + }; return [{ method: 'POST', @@ -117,7 +117,7 @@ export const spec = { } return bidResponses; } -} +}; /** * Generate size param for bid request using sizes array diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 1347fa04bd5..563435dee65 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -118,7 +118,7 @@ export const intentIqIdSubmodule = { logError('User ID - intentIqId submodule requires a valid partner to be defined'); return; } - if (!FIRST_PARTY_DATA_KEY.includes(configParams.partner)) { FIRST_PARTY_DATA_KEY += '_' + configParams.partner } + if (!FIRST_PARTY_DATA_KEY.includes(configParams.partner)) { FIRST_PARTY_DATA_KEY += '_' + configParams.partner; } let rrttStrtTime = 0; // Read Intent IQ 1st party id or generate it if none exists @@ -169,7 +169,7 @@ export const intentIqIdSubmodule = { shouldUpdateLs = true; } if (shouldUpdateLs === true) { - partnerData.date = Date.now() + partnerData.date = Date.now(); storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData)); storeData(FIRST_PARTY_DATA_KEY, JSON.stringify(partnerData)); } diff --git a/modules/interactiveOffersBidAdapter.js b/modules/interactiveOffersBidAdapter.js index 25dcd4f3cd1..1fad231dff2 100644 --- a/modules/interactiveOffersBidAdapter.js +++ b/modules/interactiveOffersBidAdapter.js @@ -169,7 +169,7 @@ function parseResponseOpenRTBToPrebidjs(openRTBResponse) { mediaType: 'banner', primaryCatId: bid.cat[0] || '', secondaryCatIds: bid.cat - } + }; prebidResponse.push(prebid); }); } diff --git a/modules/iqmBidAdapter.js b/modules/iqmBidAdapter.js index 68b027c1bec..1c36bafd3ce 100644 --- a/modules/iqmBidAdapter.js +++ b/modules/iqmBidAdapter.js @@ -245,7 +245,7 @@ function getSite(bidderRequest) { function _buildVideoORTB(bidRequest) { const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video'); const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); - const video = {} + const video = {}; const videoParams = { ...videoAdUnit, diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js index 6c7b75bbce2..876f0ebabc6 100644 --- a/modules/krushmediaBidAdapter.js +++ b/modules/krushmediaBidAdapter.js @@ -57,7 +57,7 @@ export const spec = { let location; // TODO: this odd try-catch block was copied in several adapters; it doesn't seem to be correct for cross-origin try { - location = new URL(bidderRequest.refererInfo.page) + location = new URL(bidderRequest.refererInfo.page); winTop = window.top; } catch (e) { location = winTop.location; @@ -80,7 +80,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index 2721031948d..fe69220e123 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -67,10 +67,10 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE auc: bidRequest.auc, buc: bidRequest.buc, lw: bidRequest.lw - } + }; logInfo(bidRequest); - }) + }); logInfo(livewrappedAnalyticsAdapter.requestEvents); break; case CONSTANTS.EVENTS.BID_RESPONSE: @@ -183,7 +183,7 @@ livewrappedAnalyticsAdapter.sendEvents = function() { } ajax(initOptions.endpoint || URL, undefined, JSON.stringify(events), {method: 'POST'}); -} +}; function getAdblockerRecovered() { try { diff --git a/modules/lkqdBidAdapter.js b/modules/lkqdBidAdapter.js index 6d2766ff2f1..1dbe89f5a49 100644 --- a/modules/lkqdBidAdapter.js +++ b/modules/lkqdBidAdapter.js @@ -38,7 +38,7 @@ export const spec = { const UA = navigator.userAgent; const USP = BIDDER_REQUEST.uspConsent || null; // TODO: does the fallback make sense here? - const REFERER = BIDDER_REQUEST?.refererInfo?.domain || window.location.host + const REFERER = BIDDER_REQUEST?.refererInfo?.domain || window.location.host; const BIDDER_GDPR = BIDDER_REQUEST.gdprConsent && BIDDER_REQUEST.gdprConsent.gdprApplies ? 1 : null; const BIDDER_GDPRS = BIDDER_REQUEST.gdprConsent && BIDDER_REQUEST.gdprConsent.consentString ? BIDDER_REQUEST.gdprConsent.consentString : null; @@ -74,7 +74,7 @@ export const spec = { us_privacy: USP } } - } + }; if (isSet(DNT)) { requestData.device.dnt = DNT; @@ -94,7 +94,7 @@ export const spec = { id: bid.params.aid, name: bid.params.appname, bundle: bid.params.bundleid - } + }; if (bid.params.contentId) { requestData.app.content = { diff --git a/modules/loganBidAdapter.js b/modules/loganBidAdapter.js index ec5006b7c8c..7aa82e3046c 100644 --- a/modules/loganBidAdapter.js +++ b/modules/loganBidAdapter.js @@ -72,7 +72,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } @@ -85,7 +85,7 @@ export const spec = { schain: bid.schain || {}, bidfloor: getBidFloor(bid) }; - const mediaType = bid.mediaTypes + const mediaType = bid.mediaTypes; if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { placement.sizes = mediaType[BANNER].sizes; diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 31e2120d364..835c04ba074 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -176,7 +176,7 @@ export const spec = { responses.forEach(csResp => { if (csResp.body && csResp.body.ext && csResp.body.ext.usersyncs) { try { - let response = csResp.body.ext.usersyncs + let response = csResp.body.ext.usersyncs; let bidders = response.bidder_status; for (let synci in bidders) { let thisSync = bidders[synci]; @@ -314,7 +314,7 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { }, user: { } - } + }; let bidFloor; if (isFn(bidRequest.getFloor) && !config.getConfig('disableFloors')) { @@ -566,7 +566,7 @@ function parseSizes(bid, mediaType) { } else if (typeof deepAccess(bid, 'mediaTypes.banner.sizes') !== 'undefined') { sizes = mapSizes(bid.mediaTypes.banner.sizes); } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { - sizes = mapSizes(bid.sizes) + sizes = mapSizes(bid.sizes); } else { logWarn('LuponMedia: no sizes are setup or found'); } diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 6466aa4feed..cee8e888986 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -81,7 +81,7 @@ export const spec = { data: data, gdpr_applies: gdrpApplies, gdpr_consent: gdprConsent, - } + }; return [{ method: 'POST', @@ -120,7 +120,7 @@ export const spec = { } return bidResponses; } -} +}; /** * Generate size param for bid request using sizes array diff --git a/modules/mediakeysBidAdapter.js b/modules/mediakeysBidAdapter.js index cc8ffc6d29e..8c2b2c32e1c 100644 --- a/modules/mediakeysBidAdapter.js +++ b/modules/mediakeysBidAdapter.js @@ -420,7 +420,7 @@ function createVideoImp(bid) { } }); - return video + return video; } /** @@ -703,7 +703,7 @@ export const spec = { agencyName: deepAccess(bid, 'ext.agency_name', null), primaryCatId: getPrimaryCatFromResponse(bid.cat), mediaType - } + }; const newBid = { requestId: bid.impid, diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index 56cf1e9645e..2653f157196 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -88,7 +88,7 @@ export const spec = { let v = nativeParams[k]; const supportProp = spec.NATIVE_ASSET_KEY_TO_ASSET_MAP.hasOwnProperty(k); if (supportProp) { - assetsCount++ + assetsCount++; } if (!isPlainObject(v) || (!supportProp && deepAccess(v, 'required'))) { nativeOk = false; @@ -152,7 +152,7 @@ export const spec = { impObj.bidfloor = floorData.floor; } if (floorData.cur) { - impObj.bidfloorcur = floorData.cur + impObj.bidfloorcur = floorData.cur; } for (let mediaTypes in bid.mediaTypes) { switch (mediaTypes) { diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 877afb08293..20878b545e4 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -147,7 +147,7 @@ function nobidBuildRequests(bids, bidderRequest) { if (eids && eids.length > 0) state['eids'] = eids; if (bidderRequest && bidderRequest.ortb2) state['ortb2'] = bidderRequest.ortb2; return state; - } + }; function newAdunit(adunitObject, adunits) { var getAdUnit = function(divid, adunits) { for (var i = 0; i < adunits.length; i++) { @@ -438,7 +438,7 @@ export const spec = { type: 'image', url: element }); - }) + }); } return syncs; } else { diff --git a/modules/novatiqIdSystem.js b/modules/novatiqIdSystem.js index 661a14ca0ef..7bd1ee8acd9 100644 --- a/modules/novatiqIdSystem.js +++ b/modules/novatiqIdSystem.js @@ -245,7 +245,7 @@ export const novatiqIdSubmodule = { } else { srcId = configParams.sourceid; } - return srcId + return srcId; } }; submodule('userId', novatiqIdSubmodule); diff --git a/modules/open8BidAdapter.js b/modules/open8BidAdapter.js index 7fa97235525..5fa1dd0a143 100644 --- a/modules/open8BidAdapter.js +++ b/modules/open8BidAdapter.js @@ -68,7 +68,7 @@ export const spec = { meta: { advertiserDomains: ad.adomain || [] } - } + }; if (ad.adType === AD_TYPE.VIDEO) { const videoAd = bidderResponse.ad.video; diff --git a/modules/openwebBidAdapter.js b/modules/openwebBidAdapter.js index f07b37e16e1..296bfc682f1 100644 --- a/modules/openwebBidAdapter.js +++ b/modules/openwebBidAdapter.js @@ -149,7 +149,7 @@ function bidToTag(bidRequests, adapterRequest) { tag.UserEids = deepAccess(bidRequests[0], 'userIdAsEids'); } // end publisher env - const bids = [] + const bids = []; for (let i = 0, length = bidRequests.length; i < length; i++) { const bid = prepareBidRequests(bidRequests[i]); diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 251e6fb50e3..14525cd0cfc 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -353,7 +353,7 @@ function appendUserIdsToQueryParams(queryParams, userIds) { case 'lipb': queryParams[key] = userIdObjectOrValue.lipbid; if (Array.isArray(userIdObjectOrValue.segments) && userIdObjectOrValue.segments.length > 0) { - const liveIntentSegments = 'liveintent:' + userIdObjectOrValue.segments.join('|') + const liveIntentSegments = 'liveintent:' + userIdObjectOrValue.segments.join('|'); queryParams.sm = `${queryParams.sm ? queryParams.sm + ',' : ''}${liveIntentSegments}`; } break; @@ -502,7 +502,7 @@ function generateVideoParameters(bid, bidderRequest) { video: openRtbParams } ] - } + }; queryParams['openrtb'] = JSON.stringify(openRtbReq); @@ -525,7 +525,7 @@ function generateVideoParameters(bid, bidderRequest) { let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { - queryParams.aucs = encodeURIComponent(gpid) + queryParams.aucs = encodeURIComponent(gpid); } // each video bid makes a separate request diff --git a/modules/pixfutureBidAdapter.js b/modules/pixfutureBidAdapter.js index bffd5badded..4c4935e93a4 100644 --- a/modules/pixfutureBidAdapter.js +++ b/modules/pixfutureBidAdapter.js @@ -211,7 +211,7 @@ function bidToTag(bid) { tag.code = bid.params.invCode; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; let bidFloor = getBidFloor(bid); diff --git a/modules/prebidmanagerAnalyticsAdapter.js b/modules/prebidmanagerAnalyticsAdapter.js index 708d2b79243..1180a74db30 100644 --- a/modules/prebidmanagerAnalyticsAdapter.js +++ b/modules/prebidmanagerAnalyticsAdapter.js @@ -9,7 +9,7 @@ import CONSTANTS from '../src/constants.json'; * prebidmanagerAnalyticsAdapter.js - analytics adapter for prebidmanager */ export const storage = getStorageManager({gvlid: undefined, moduleName: 'prebidmanager'}); -const DEFAULT_EVENT_URL = 'https://endpoint.prebidmanager.com/endpoint' +const DEFAULT_EVENT_URL = 'https://endpoint.prebidmanager.com/endpoint'; const analyticsType = 'endpoint'; const analyticsName = 'Prebid Manager Analytics: '; diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 900276e6b4e..c972e024780 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -700,7 +700,7 @@ export function addBidResponseHook(fn, adUnitCode, bid) { return fn.call(this, adUnitCode, bid); } - const matchingBidRequest = auctionManager.index.getBidRequest(bid) + const matchingBidRequest = auctionManager.index.getBidRequest(bid); // get the matching rule let floorInfo = getFirstMatchingFloor(floorData.data, matchingBidRequest, {...bid, size: [bid.width, bid.height]}); diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index 7976bcef58d..19a74c7c245 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -295,7 +295,7 @@ pubwiseAnalytics.handleEvent = function(eventType, data) { if (eventType === CONSTANTS.EVENTS.AUCTION_END || eventType === CONSTANTS.EVENTS.BID_WON) { flushEvents(); } -} +}; pubwiseAnalytics.storeSessionID = function (userSessID) { storage.setDataInLocalStorage(localStorageSessName(), userSessID); diff --git a/modules/pubwiseBidAdapter.js b/modules/pubwiseBidAdapter.js index 1c015a24536..7721fe10459 100644 --- a/modules/pubwiseBidAdapter.js +++ b/modules/pubwiseBidAdapter.js @@ -161,7 +161,7 @@ export const spec = { } if (bid.params.isTest) { - payload.test = Number(bid.params.isTest) // should be 1 or 0 + payload.test = Number(bid.params.isTest); // should be 1 or 0 } payload.site.publisher.id = bid.params.siteId.trim(); payload.user.gender = (conf.gender ? conf.gender.trim() : UNDEFINED); @@ -207,7 +207,7 @@ export const spec = { deepSetValue(payload, 'regs.coppa', 1); } - var options = {contentType: 'text/plain'} + var options = {contentType: 'text/plain'}; _logInfo('buildRequests payload', payload); _logInfo('buildRequests bidderRequest', bidderRequest); @@ -463,7 +463,7 @@ function _createImpressionObject(bid, conf) { } } } else { - _logWarn('MediaTypes are Required for all Adunit Configs', bid) + _logWarn('MediaTypes are Required for all Adunit Configs', bid); } _addFloorFromFloorModule(impObj, bid); diff --git a/modules/pubxBidAdapter.js b/modules/pubxBidAdapter.js index 18d2bb11404..17c4ba3848e 100644 --- a/modules/pubxBidAdapter.js +++ b/modules/pubxBidAdapter.js @@ -76,7 +76,7 @@ export const spec = { } else { kwString = kwContents; } - kwEnc = encodeURIComponent(kwString) + kwEnc = encodeURIComponent(kwString); } else { } if (titleContent) { if (titleContent.length > 30) { diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index bd92162b8cb..f7e73dd5fdd 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -140,7 +140,7 @@ export function getOS() { // add sampling rate pubxaiAnalyticsAdapter.shouldFireEventRequest = function (samplingRate = 1) { return (Math.floor((Math.random() * samplingRate + 1)) === parseInt(samplingRate)); -} +}; function send(data, status) { if (pubxaiAnalyticsAdapter.shouldFireEventRequest(initOptions.samplingRate)) { @@ -158,7 +158,7 @@ function send(data, status) { data.initOptions.auctionId = data.auctionInit.auctionId; delete data.auctionInit; - data.pmcDetail = {} + data.pmcDetail = {}; Object.assign(data.pmcDetail, { bidDensity: storage ? storage.getItem('pbx:dpbid') : null, maxBid: storage ? storage.getItem('pbx:mxbid') : null, diff --git a/modules/quantcastIdSystem.js b/modules/quantcastIdSystem.js index b803096cd31..97cd01f98da 100644 --- a/modules/quantcastIdSystem.js +++ b/modules/quantcastIdSystem.js @@ -211,7 +211,7 @@ export const quantcastIdSubmodule = { }); } - return { id: fpa ? { quantcastId: fpa } : undefined } + return { id: fpa ? { quantcastId: fpa } : undefined }; } }; diff --git a/modules/radsBidAdapter.js b/modules/radsBidAdapter.js index 938fd566159..fcb8c3a8c2a 100644 --- a/modules/radsBidAdapter.js +++ b/modules/radsBidAdapter.js @@ -65,7 +65,7 @@ export const spec = { method: 'GET', url: endpoint, data: objectToQueryString(payload), - } + }; }); }, interpretResponse: function(serverResponse, bidRequest) { diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js index c7c82cd323e..5e33a9a0fd7 100644 --- a/modules/revcontentBidAdapter.js +++ b/modules/revcontentBidAdapter.js @@ -232,7 +232,7 @@ function buildImp(bid, id) { w: sizes[0][0], h: sizes[0][1], format: sizes.map(wh => parseGPTSingleSizeArrayToRtbSize(wh)), - } + }; } else if (nativeReq) { const assets = _map(bid.nativeParams, (bidParams, key) => { const props = NATIVE_PARAMS[key]; diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 564b67641a2..1faed74d84e 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -132,7 +132,7 @@ export const spec = { bidResponses.push(bidResponse); } - return bidResponses + return bidResponses; }, /*** * User Syncs diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 1ae73e192b8..961f0fcacae 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -68,7 +68,7 @@ export const spec = { if (schain) { request.ext = { schain: schain, - } + }; } } diff --git a/modules/schain.js b/modules/schain.js index d409e74df48..ef1c6da78ce 100644 --- a/modules/schain.js +++ b/modules/schain.js @@ -19,7 +19,7 @@ _each(MODE, mode => MODES.push(mode)); // validate the supply chain object export function isSchainObjectValid(schainObject, returnOnError) { - let failPrefix = 'Detected something wrong within an schain config:' + let failPrefix = 'Detected something wrong within an schain config:'; let failMsg = ''; function appendFailMsg(msg) { diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index 98451ebbb2f..f9ce3a450cf 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -314,7 +314,7 @@ function outstreamRender(bid) { logError('[ShowHeroes][renderer] Error: spot not found'); } } catch (err) { - logError('[ShowHeroes][renderer] Error:' + err.message) + logError('[ShowHeroes][renderer] Error:' + err.message); } } } diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 4a1518ab72e..05475b3e143 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -98,7 +98,7 @@ export function checkAdUnitSetupHook(adUnits) { */ if (!isArrayOfNums(config.minViewPort, 2)) { logError(`Ad unit ${adUnitCode}: Invalid declaration of 'minViewPort' in 'mediaTypes.${mediaType}.sizeConfig[${index}]'. ${conditionalLogMessages[mediaType]}`); - isValid = false + isValid = false; return; } /* diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index a24f296102f..c783f35d915 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -63,7 +63,7 @@ const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { if (deepAccess(bidRequest, 'params.app')) { const geo = deepAccess(bidRequest, 'params.app.geo'); deepSetValue(requestTemplate, 'device.geo', geo); - const ifa = deepAccess(bidRequest, 'params.app.ifa') + const ifa = deepAccess(bidRequest, 'params.app.ifa'); deepSetValue(requestTemplate, 'device.ifa', ifa); } @@ -210,7 +210,7 @@ export const spec = { } }; - const videoContext = deepAccess(JSON.parse(bidRequest.data).imp[0], 'video.ext.context') + const videoContext = deepAccess(JSON.parse(bidRequest.data).imp[0], 'video.ext.context'); if (videoContext === ADPOD) { resultingBid.vastXml = bid.adm; resultingBid.mediaType = VIDEO; diff --git a/modules/sonobiAnalyticsAdapter.js b/modules/sonobiAnalyticsAdapter.js index 75d481aba9e..0057944b201 100644 --- a/modules/sonobiAnalyticsAdapter.js +++ b/modules/sonobiAnalyticsAdapter.js @@ -255,7 +255,7 @@ sonobiAdapter.sendData = function (auction, data) { contentType: 'text/plain' } ); -} +}; function _logInfo(message, meta) { logInfo(buildLogMessage(message), meta); diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 5309dbdad5a..c6100ac8b40 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -129,7 +129,7 @@ export const spec = { } if (validBidRequests[0].schain) { - payload.schain = JSON.stringify(validBidRequests[0].schain) + payload.schain = JSON.stringify(validBidRequests[0].schain); } if (deepAccess(validBidRequests[0], 'userId') && Object.keys(validBidRequests[0].userId).length > 0) { const userIds = deepClone(validBidRequests[0].userId); @@ -217,7 +217,7 @@ export const spec = { ] = bid.sbi_size.split('x'); let aDomains = []; if (bid.sbi_adomain) { - aDomains = [bid.sbi_adomain] + aDomains = [bid.sbi_adomain]; } const bids = { requestId: bidId, @@ -255,7 +255,7 @@ export const spec = { )); let videoSize = deepAccess(bidRequest, 'params.sizes'); if (Array.isArray(videoSize) && Array.isArray(videoSize[0])) { // handle case of multiple sizes - videoSize = videoSize[0] // Only take the first size for outstream + videoSize = videoSize[0]; // Only take the first size for outstream } if (videoSize) { bids.width = videoSize[0]; diff --git a/modules/staqAnalyticsAdapter.js b/modules/staqAnalyticsAdapter.js index 69d23a3a604..13996adfb7e 100644 --- a/modules/staqAnalyticsAdapter.js +++ b/modules/staqAnalyticsAdapter.js @@ -21,7 +21,7 @@ const STAQ_EVENTS = { BID_WON: 'bidWon', AUCTION_END: 'auctionEnd', TIMEOUT: 'adapterTimedOut' -} +}; function buildRequestTemplate(connId) { // TODO: what should these pick from refererInfo? diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index b8f7a5559f8..3cd77e7b853 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -295,9 +295,9 @@ function buildOneRequest(validBidRequests, bidderRequest) { if ( ((bannerMediaType.sizes[0].indexOf(480) >= 0) && (bannerMediaType.sizes[0].indexOf(320) >= 0)) || ((bannerMediaType.sizes[0].indexOf(768) >= 0) && (bannerMediaType.sizes[0].indexOf(1024) >= 0))) { - banner.pos = 7 + banner.pos = 7; } else { - banner.pos = 4 + banner.pos = 4; } banner.api = api; diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js index f8cb58218cd..bc775e78647 100644 --- a/modules/targetVideoBidAdapter.js +++ b/modules/targetVideoBidAdapter.js @@ -132,7 +132,7 @@ function createVideoTag(bid) { tag.ad_types = [VIDEO]; tag.uuid = bid.bidId; tag.allow_smaller_sizes = false; - tag.use_pmt_rule = false + tag.use_pmt_rule = false; tag.prebid = true; tag.disable_psa = true; tag.hb_source = 1; diff --git a/modules/theAdxBidAdapter.js b/modules/theAdxBidAdapter.js index 03c6ea62cb7..3f7823d2fd1 100644 --- a/modules/theAdxBidAdapter.js +++ b/modules/theAdxBidAdapter.js @@ -206,7 +206,7 @@ export const spec = { let bidWidth = nullify(bid.w); let bidHeight = nullify(bid.h); - let creative = null + let creative = null; let videoXml = null; let mediaType = null; let native = null; @@ -389,7 +389,7 @@ let extractValidSize = (bidRequest, bidderRequest) => { requestedSizes = mediaTypes.video.sizes; } } else if (!isEmpty(bidRequest.sizes)) { - requestedSizes = bidRequest.sizes + requestedSizes = bidRequest.sizes; } // Ensure the size array is normalized diff --git a/modules/tpmnBidAdapter.js b/modules/tpmnBidAdapter.js index 8687ab06f4c..4dcdf1ef529 100644 --- a/modules/tpmnBidAdapter.js +++ b/modules/tpmnBidAdapter.js @@ -87,7 +87,7 @@ export const spec = { syncArr.push({ type: 'iframe', url: IFRAMESYNC + policyParam - }) + }); } else { syncArr.push({ type: 'image', diff --git a/modules/trionBidAdapter.js b/modules/trionBidAdapter.js index 5750406116b..b6375243a5a 100644 --- a/modules/trionBidAdapter.js +++ b/modules/trionBidAdapter.js @@ -53,7 +53,7 @@ export const spec = { bid.currency = result.currency; bid.netRevenue = result.netRevenue; if (result.adomain) { - bid.meta = {advertiserDomains: result.adomain} + bid.meta = {advertiserDomains: result.adomain}; } bidResponses.push(bid); } @@ -125,7 +125,7 @@ function buildTrionUrlParams(bid, bidderRequest) { intT = getStorageData(BASE_KEY + 'int_t'); } if (intT) { - setStorageData(BASE_KEY + 'int_t', intT) + setStorageData(BASE_KEY + 'int_t', intT); } setStorageData(BASE_KEY + 'lps', pubId + ':' + sectionId); var trionUrl = ''; diff --git a/modules/trustpidSystem.js b/modules/trustpidSystem.js index fc2bab19913..9d457d645b2 100644 --- a/modules/trustpidSystem.js +++ b/modules/trustpidSystem.js @@ -88,7 +88,7 @@ function getTrustpidFromStorage() { return { trustpid: null, acr: null - } + }; } let fcIdConnectObject; diff --git a/modules/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index 2ca4de7a555..53fe83b1dd0 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -98,7 +98,7 @@ export const spec = { } var sizeNotFound = true; - const bidParamSizes = bidParam.mediaTypes && bidParam.mediaTypes.banner && bidParam.mediaTypes.banner.sizes ? bidParam.mediaTypes.banner.sizes : bidParam.sizes + const bidParamSizes = bidParam.mediaTypes && bidParam.mediaTypes.banner && bidParam.mediaTypes.banner.sizes ? bidParam.mediaTypes.banner.sizes : bidParam.sizes; parseSizesInput(bidParamSizes).forEach(size => { if (size === mid.width + 'x' + mid.height) { sizeNotFound = false; diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js index e149a56f988..a57eae5f328 100644 --- a/modules/vdoaiBidAdapter.js +++ b/modules/vdoaiBidAdapter.js @@ -102,7 +102,7 @@ export const spec = { if (response.adDomain) { bidResponse.meta = { advertiserDomains: response.adDomain - } + }; } bidResponses.push(bidResponse); } diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 1f84c9f4b16..39d37d55a91 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -97,7 +97,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { } } if (bidderRequest.uspConsent) { - data.usPrivacy = bidderRequest.uspConsent + data.usPrivacy = bidderRequest.uspConsent; } const dto = { diff --git a/modules/vidoomyBidAdapter.js b/modules/vidoomyBidAdapter.js index ebecb5a46ae..146ea6dd4bd 100644 --- a/modules/vidoomyBidAdapter.js +++ b/modules/vidoomyBidAdapter.js @@ -110,7 +110,7 @@ const buildRequests = (validBidRequests, bidderRequest) => { method: 'GET', url: ENDPOINT, data: queryParams - } + }; }); return serverRequests; }; diff --git a/modules/winrBidAdapter.js b/modules/winrBidAdapter.js index 3b86e5b59dc..cd807917944 100644 --- a/modules/winrBidAdapter.js +++ b/modules/winrBidAdapter.js @@ -208,7 +208,7 @@ export const spec = { } if (appDeviceObjBid) { - payload.device = appDeviceObj + payload.device = appDeviceObj; } if (appIdObjBid) { payload.app = appIdObj; diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index d1b7b08c202..38bb141070c 100644 --- a/modules/yahoosspBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -408,7 +408,7 @@ function appendFirstPartyData(outBoundBidRequest, bid) { newDataObject = validateAppendObject('object', allowedContentDataObjectKeys, dataObject, newDataObject); outBoundBidRequest.site.content.data = []; outBoundBidRequest.site.content.data.push(newDataObject); - }) + }); }; }; @@ -428,7 +428,7 @@ function appendFirstPartyData(outBoundBidRequest, bid) { } }; outBoundBidRequest.app.content.data.push(newDataObject); - }) + }); }; }; diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index ae2fb16c8ac..7da2872a3a6 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -65,7 +65,7 @@ export const spec = { withCredentials, }, bidRequest, - } + }; }); }, diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index a0d634e34b4..3149dcb7f5c 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -531,13 +531,13 @@ function validateVideoParams(bid) { error += ' when ' + conditionStr; } throw new Error(error); - } + }; const paramInvalid = (paramStr, value, expectedStr) => { expectedStr = expectedStr ? ', expected: ' + expectedStr : ''; value = JSON.stringify(value); throw new Error(`"${paramStr}"=${value} is invalid${expectedStr}`); - } + }; const isDefined = val => typeof val !== 'undefined'; const validate = (fieldPath, validateCb, errorCb, errorCbParam) => { @@ -563,7 +563,7 @@ function validateVideoParams(bid) { } return value; } - } + }; try { validate('video.context', val => !isEmpty(val), paramRequired); diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index 98a95b6758e..c07911c9e1f 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -90,7 +90,7 @@ export const spec = { method: 'GET', url: ENDPOINT_URL, data: payload, - } + }; }); }, interpretResponse: function(serverResponse, bidRequest) { @@ -212,7 +212,7 @@ function getMediaType(bidRequest, enabledOldFormat = true) { } if (hasBannerType && hasVideoType) { - const playerParams = deepAccess(bidRequest, 'params.playerParams') + const playerParams = deepAccess(bidRequest, 'params.playerParams'); if (playerParams) { return VIDEO; } else { diff --git a/src/Renderer.js b/src/Renderer.js index 3375edcd6c3..97e37084e89 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -60,7 +60,7 @@ export function Renderer(options) { this.cmd.unshift(runRender) // should render run first ? loadExternalScript(url, moduleCode, this.callback, this.documentContext); } - }.bind(this) // bind the function to this object to avoid 'this' errors + }.bind(this); // bind the function to this object to avoid 'this' errors } Renderer.install = function({ url, config, id, callback, loaded, adUnitCode, renderNow }) { diff --git a/src/bidfactory.js b/src/bidfactory.js index 95d69cf0adb..4c2e4cf3ffb 100644 --- a/src/bidfactory.js +++ b/src/bidfactory.js @@ -59,7 +59,7 @@ function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, auc transactionId: this.transactionId, auctionId: this.auctionId } - } + }; } // Bid factory function. diff --git a/src/sizeMapping.js b/src/sizeMapping.js index 4333608ca95..cfd2861785c 100644 --- a/src/sizeMapping.js +++ b/src/sizeMapping.js @@ -111,7 +111,7 @@ export function resolveStatus({labels = [], labelAll = false, activeLabels = []} results.filterResults = { before: oldSizes, after: mediaTypes.banner.sizes - } + }; } return results; diff --git a/src/targeting.js b/src/targeting.js index cb10b3f0121..d2b3e9f8470 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -63,7 +63,7 @@ export const getHighestCpmBidsFromBidPool = hook('sync', function(bidsReceived, } return bidsReceived; -}) +}); /** * A descending sort function that will sort the list of objects based on the following two dimensions: diff --git a/src/userSync.js b/src/userSync.js index 674114e11f6..dec8f650930 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -311,7 +311,7 @@ export function newUserSync(userSyncDependencies) { } } return true; - } + }; return publicApi; } From 2bc9f9d4eb7671596b4d04df77d796d64470d056 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Tue, 16 Aug 2022 15:45:11 +0600 Subject: [PATCH 147/569] Zeta ssp adapter: added multiimp request support (#8813) * multiimp ability * add some test * fix tests * fix tests 2 Co-authored-by: Surovenko Alexey --- modules/zeta_global_sspBidAdapter.js | 45 +++++++------- .../modules/zeta_global_sspBidAdapter_spec.js | 59 +++++++++++++++++++ 2 files changed, 83 insertions(+), 21 deletions(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index d80265b28bb..4689683fbc7 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -69,31 +69,34 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { const secure = 1; // treat all requests as secure - const request = validBidRequests[0]; - const params = request.params; - const impData = { - id: request.bidId, - secure: secure - }; - if (request.mediaTypes) { - for (const mediaType in request.mediaTypes) { - switch (mediaType) { - case BANNER: - impData.banner = buildBanner(request); - break; - case VIDEO: - impData.video = buildVideo(request); - break; + const params = validBidRequests[0].params; + const imps = validBidRequests.map(request => { + const impData = { + id: request.bidId, + secure: secure + }; + if (request.mediaTypes) { + for (const mediaType in request.mediaTypes) { + switch (mediaType) { + case BANNER: + impData.banner = buildBanner(request); + break; + case VIDEO: + impData.video = buildVideo(request); + break; + } } } - } - if (!impData.banner && !impData.video) { - impData.banner = buildBanner(request); - } + if (!impData.banner && !impData.video) { + impData.banner = buildBanner(request); + } + return impData; + }); + let payload = { id: bidderRequest.auctionId, cur: [DEFAULT_CUR], - imp: [impData], + imp: imps, site: params.site ? params.site : {}, device: {...(bidderRequest.ortb2?.device || {}), ...params.device}, user: params.user ? params.user : {}, @@ -126,7 +129,7 @@ export const spec = { deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - provideEids(request, payload); + provideEids(validBidRequests[0], payload); const url = params.shortname ? ENDPOINT_URL.concat('?shortname=', params.shortname) : ENDPOINT_URL; return { method: 'POST', diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index a70c1c79828..3bd17697f2d 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -44,6 +44,48 @@ describe('Zeta Ssp Bid Adapter', function () { test: 1 }; + const multiImpRequest = [ + { + bidId: 12345, + auctionId: 67890, + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + refererInfo: { + page: 'http://www.zetaglobal.com/page?param=value', + domain: 'www.zetaglobal.com', + }, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentString' + }, + uspConsent: 'someCCPAString', + params: params, + userIdAsEids: eids + }, { + bidId: 54321, + auctionId: 67890, + mediaTypes: { + banner: { + sizes: [[600, 400]], + } + }, + refererInfo: { + page: 'http://www.zetaglobal.com/page?param=value', + domain: 'www.zetaglobal.com', + }, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentString' + }, + uspConsent: 'someCCPAString', + params: params, + userIdAsEids: eids + } + ]; + const bannerRequest = [{ bidId: 12345, auctionId: 67890, @@ -285,4 +327,21 @@ describe('Zeta Ssp Bid Adapter', function () { expect(payload.ext.tags.someTag).to.eql(444); expect(payload.ext.tags.shortname).to.be.undefined; }); + + it('Test multi imp', function () { + const request = spec.buildRequests(multiImpRequest, multiImpRequest[0]); + const payload = JSON.parse(request.data); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); + + expect(payload.imp.length).to.eql(2); + + expect(payload.imp[0].id).to.eql(12345); + expect(payload.imp[1].id).to.eql(54321); + + expect(payload.imp[0].banner.w).to.eql(300); + expect(payload.imp[0].banner.h).to.eql(250); + + expect(payload.imp[1].banner.w).to.eql(600); + expect(payload.imp[1].banner.h).to.eql(400); + }); }); From 9f01c38ba5e7074b3358a10003d0bb7363ce35ed Mon Sep 17 00:00:00 2001 From: vishal-dw <109065778+vishal-dw@users.noreply.github.com> Date: Tue, 16 Aug 2022 19:24:07 +0530 Subject: [PATCH 148/569] Datawrkz Bid Adapter: initial adapter release (#8754) * New Bid Adapter: datawrkz * New Bid Adapter: datawrkz. Test case formatting * New Bid Adatpter: datawrkz - updated import statements --- modules/datawrkzBidAdapter.js | 633 ++++++++++++++++++ modules/datawrkzBidAdapter.md | 160 +++++ test/spec/modules/datawrkzBidAdapter_spec.js | 656 +++++++++++++++++++ 3 files changed, 1449 insertions(+) create mode 100644 modules/datawrkzBidAdapter.js create mode 100644 modules/datawrkzBidAdapter.md create mode 100644 test/spec/modules/datawrkzBidAdapter_spec.js diff --git a/modules/datawrkzBidAdapter.js b/modules/datawrkzBidAdapter.js new file mode 100644 index 00000000000..a4ee6bcaace --- /dev/null +++ b/modules/datawrkzBidAdapter.js @@ -0,0 +1,633 @@ +import { deepAccess, getBidIdParameter, isArray, getUniqueIdentifierStr, contains } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { createBid } from '../src/bidfactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import CONSTANTS from '../src/constants.json'; +import { OUTSTREAM, INSTREAM } from '../src/video.js'; + +const BIDDER_CODE = 'datawrkz'; +const ALIASES = []; +const ENDPOINT_URL = 'https://at.datawrkz.com/exchange/openrtb23/'; +const RENDERER_URL = 'https://js.datawrkz.com/prebid/osRenderer.min.js'; +const OUTSTREAM_TYPES = ['inline', 'slider_top_left', 'slider_top_right', 'slider_bottom_left', 'slider_bottom_right', 'interstitial_close', 'listicle'] +const OUTSTREAM_MIMES = ['video/mp4'] +const SUPPORTED_AD_TYPES = [BANNER, NATIVE, VIDEO]; + +export const spec = { + code: BIDDER_CODE, + aliases: ALIASES, + supportedMediaTypes: SUPPORTED_AD_TYPES, + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + return !!(bid.params && bid.params.site_id && (deepAccess(bid, 'mediaTypes.video.context') != 'adpod')); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + let requests = []; + + if (validBidRequests.length > 0) { + validBidRequests.forEach(bidRequest => { + if (!bidRequest.mediaTypes) return; + if (bidRequest.mediaTypes.banner && ((bidRequest.mediaTypes.banner.sizes && bidRequest.mediaTypes.banner.sizes.length != 0) || + (bidRequest.sizes))) { + requests.push(buildBannerRequest(bidRequest, bidderRequest)); + } else if (bidRequest.mediaTypes.native) { + requests.push(buildNativeRequest(bidRequest, bidderRequest)); + } else if (bidRequest.mediaTypes.video) { + requests.push(buildVideoRequest(bidRequest, bidderRequest)); + } + }); + } + return requests; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, request) { + var bidResponses = []; + let bidRequest = request.bidRequest + let bidResponse = serverResponse.body; + + // valid object? + if ((!bidResponse || !bidResponse.id) || (!bidResponse.seatbid || bidResponse.seatbid.length === 0 || + !bidResponse.seatbid[0].bid || bidResponse.seatbid[0].bid.length === 0)) { + return []; + } + + if (getMediaTypeOfResponse(bidRequest) == BANNER) { + bidResponses = buildBannerResponse(bidRequest, bidResponse); + } else if (getMediaTypeOfResponse(bidRequest) == NATIVE) { + bidResponses = buildNativeResponse(bidRequest, bidResponse); + } else if (getMediaTypeOfResponse(bidRequest) == VIDEO) { + bidResponses = buildVideoResponse(bidRequest, bidResponse); + } + return bidResponses; + }, +} + +/* Generate bid request for banner adunit */ +function buildBannerRequest(bidRequest, bidderRequest) { + let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + + let adW = 0; + let adH = 0; + + let bannerSizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes'); + let bidSizes = isArray(bannerSizes) ? bannerSizes : bidRequest.sizes; + if (isArray(bidSizes)) { + if (bidSizes.length === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') { + adW = parseInt(bidSizes[0]); + adH = parseInt(bidSizes[1]); + } else { + adW = parseInt(bidSizes[0][0]); + adH = parseInt(bidSizes[0][1]); + } + } + + var deals = []; + if (bidRequest.params.deals && bidRequest.params.deals.length > 0) { + deals = bidRequest.params.deals; + } + + const imp = [{ + id: bidRequest.bidId, + banner: { + w: adW, + h: adH + }, + bidfloor: bidFloor, + pmp: { + deals: deals + } + }]; + + bidRequest.requestedMediaType = BANNER; + const scriptUrl = generateScriptUrl(bidRequest); + const payloadString = generatePayload(imp, bidderRequest); + + return { + method: 'POST', + url: scriptUrl, + data: payloadString, + bidRequest + }; +} + +/* Generate bid request for native adunit */ +function buildNativeRequest(bidRequest, bidderRequest) { + let counter = 0; + let assets = []; + + let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + + let title = deepAccess(bidRequest, 'mediaTypes.native.title'); + if (title && title.len) { + assets.push(generateNativeTitleObj(title, ++counter)); + } + let image = deepAccess(bidRequest, 'mediaTypes.native.image'); + if (image) { + assets.push(generateNativeImgObj(image, 'image', ++counter)); + } + let icon = deepAccess(bidRequest, 'mediaTypes.native.icon'); + if (icon) { + assets.push(generateNativeImgObj(icon, 'icon', ++counter)); + } + let sponsoredBy = deepAccess(bidRequest, 'mediaTypes.native.sponsoredBy'); + if (sponsoredBy) { + assets.push(generateNativeDataObj(sponsoredBy, 'sponsored', ++counter)); + } + let cta = deepAccess(bidRequest, 'mediaTypes.native.cta'); + if (cta) { + assets.push(generateNativeDataObj(cta, 'cta', ++counter)); + } + let body = deepAccess(bidRequest, 'mediaTypes.native.body'); + if (body) { + assets.push(generateNativeDataObj(body, 'desc', ++counter)); + } + + let request = JSON.stringify({assets: assets}) + const native = { + request: request + } + + var deals = []; + if (bidRequest.params.deals && bidRequest.params.deals.length > 0) { + deals = bidRequest.params.deals; + } + + const imp = [{ + id: bidRequest.bidId, + native: native, + bidfloor: bidFloor, + pmp: { + deals: deals + } + }]; + + bidRequest.requestedMediaType = NATIVE; + bidRequest.assets = assets; + const scriptUrl = generateScriptUrl(bidRequest); + const payloadString = generatePayload(imp, bidderRequest); + + return { + method: 'POST', + url: scriptUrl, + data: payloadString, + bidRequest + }; +} + +/* Generate bid request for video adunit */ +function buildVideoRequest(bidRequest, bidderRequest) { + let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + + let sizeObj = getVideoAdUnitSize(bidRequest); + + const video = { + w: sizeObj.adW, + h: sizeObj.adH, + api: deepAccess(bidRequest, 'mediaTypes.video.api'), + mimes: deepAccess(bidRequest, 'mediaTypes.video.mimes'), + protocols: deepAccess(bidRequest, 'mediaTypes.video.protocols'), + playbackmethod: deepAccess(bidRequest, 'mediaTypes.video.playbackmethod'), + minduration: deepAccess(bidRequest, 'mediaTypes.video.minduration'), + maxduration: deepAccess(bidRequest, 'mediaTypes.video.maxduration'), + startdelay: deepAccess(bidRequest, 'mediaTypes.video.startdelay'), + minbitrate: deepAccess(bidRequest, 'mediaTypes.video.minbitrate'), + maxbitrate: deepAccess(bidRequest, 'mediaTypes.video.maxbitrate'), + delivery: deepAccess(bidRequest, 'mediaTypes.video.delivery'), + linearity: deepAccess(bidRequest, 'mediaTypes.video.linearity'), + placement: deepAccess(bidRequest, 'mediaTypes.video.placement'), + skip: deepAccess(bidRequest, 'mediaTypes.video.skip'), + skipafter: deepAccess(bidRequest, 'mediaTypes.video.skipafter') + }; + + let context = deepAccess(bidRequest, 'mediaTypes.video.context'); + if (context == 'outstream' && !bidRequest.renderer) video.mimes = OUTSTREAM_MIMES + + var imp = []; + var deals = []; + if (bidRequest.params.deals && bidRequest.params.deals.length > 0) { + deals = bidRequest.params.deals; + } + + if (context != 'adpod') { + imp.push({ + id: bidRequest.bidId, + video: video, + bidfloor: bidFloor, + pmp: { + deals: deals + } + }); + } + bidRequest.requestedMediaType = VIDEO; + const scriptUrl = generateScriptUrl(bidRequest); + const payloadString = generatePayload(imp, bidderRequest); + + return { + method: 'POST', + url: scriptUrl, + data: payloadString, + bidRequest + }; +} + +/* Convert video player size to bid request compatible format */ +function getVideoAdUnitSize(bidRequest) { + var adH = 0; + var adW = 0; + let playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + if (isArray(playerSize)) { + if (playerSize.length === 2 && typeof playerSize[0] === 'number' && typeof playerSize[1] === 'number') { + adW = parseInt(playerSize[0]); + adH = parseInt(playerSize[1]); + } else { + adW = parseInt(playerSize[0][0]); + adH = parseInt(playerSize[0][1]); + } + } + return {adH: adH, adW: adW} +} + +/* Get mediatype of the adunit from request */ +function getMediaTypeOfResponse(bidRequest) { + if (bidRequest.requestedMediaType == BANNER) return BANNER; + else if (bidRequest.requestedMediaType == NATIVE) return NATIVE; + else if (bidRequest.requestedMediaType == VIDEO) return VIDEO; + else return ''; +} + +/* Generate endpoint url */ +function generateScriptUrl(bidRequest) { + let queryParams = 'hb=1'; + let siteId = getBidIdParameter('site_id', bidRequest.params); + return ENDPOINT_URL + siteId + '?' + queryParams; +} + +/* Generate request payload for the adunit */ +function generatePayload(imp, bidderRequest) { + let domain = window.location.host; + let page = window.location.host + window.location.pathname + location.search + location.hash; + + const site = { + domain: domain, + page: page, + publisher: {} + }; + + let regs = {ext: {}} + + if (bidderRequest.uspConsent) { + regs.ext.us_privacy = bidderRequest.uspConsent; + } + if (bidderRequest.gdprConsent && typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { + regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? '1' : '0'; + } + + if (config.getConfig('coppa') === true) { + regs.coppa = '1'; + } + + const device = { + ua: window.navigator.userAgent + }; + + const payload = { + id: getUniqueIdentifierStr(), + imp: imp, + site: site, + device: device, + regs: regs + }; + + return JSON.stringify(payload); +} + +/* Generate image asset object */ +function generateNativeImgObj(obj, type, id) { + let adW = 0; + let adH = 0; + let bidSizes = obj.sizes; + + var typeId; + if (type == 'icon') typeId = 1; + else if (type == 'image') typeId = 3; + + if (isArray(bidSizes)) { + if (bidSizes.length === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') { + adW = parseInt(bidSizes[0]); + adH = parseInt(bidSizes[1]); + } else { + adW = parseInt(bidSizes[0][0]); + adH = parseInt(bidSizes[0][1]); + } + } + + let required = obj.required ? 1 : 0; + let image = { + type: parseInt(typeId), + w: adW, + h: adH + } + return { + id: id, + required: required, + img: image + }; +} + +/* Generate title asset object */ +function generateNativeTitleObj(obj, id) { + let required = obj.required ? 1 : 0; + let title = { + len: obj.len + }; + return { + id: id, + required: required, + title: title + }; +} + +/* Generate data asset object */ +function generateNativeDataObj(obj, type, id) { + var typeId; + switch (type) { + case 'sponsored': typeId = 1; + break; + case 'desc' : typeId = 2; + break; + case 'cta' : typeId = 12; + break; + } + + let required = obj.required ? 1 : 0; + let data = { + type: typeId + }; + if (typeId == 2 && obj.len) { + data.len = parseInt(obj.len); + } + return { + id: id, + required: required, + data: data + }; +} + +/* Convert banner bid response to compatible format */ +function buildBannerResponse(bidRequest, bidResponse) { + const bidResponses = []; + bidResponse.seatbid[0].bid.forEach(function (bidderBid) { + let responseCPM; + let placementCode = ''; + + if (bidRequest) { + let bidResponse = createBid(1); + placementCode = bidRequest.placementCode; + bidRequest.status = CONSTANTS.STATUS.GOOD; + responseCPM = parseFloat(bidderBid.price); + if (responseCPM === 0 || isNaN(responseCPM)) { + let bid = createBid(2); + bid.requestId = bidRequest.bidId; + bid.bidderCode = bidRequest.bidder; + bidResponses.push(bid); + return; + } + let bidSizes = (deepAccess(bidRequest, 'mediaTypes.banner.sizes')) ? deepAccess(bidRequest, 'mediaTypes.banner.sizes') : bidRequest.sizes; + bidResponse.requestId = bidRequest.bidId + bidResponse.transactionId = bidRequest.transactionId + bidResponse.placementCode = placementCode; + bidResponse.cpm = responseCPM; + bidResponse.size = bidSizes; + bidResponse.width = parseInt(bidderBid.w); + bidResponse.height = parseInt(bidderBid.h); + let responseAd = bidderBid.adm; + let responseNurl = ''; + bidResponse.ad = decodeURIComponent(responseAd + responseNurl); + bidResponse.creativeId = bidderBid.id; + bidResponse.bidderCode = bidRequest.bidder; + bidResponse.ttl = 300; + bidResponse.netRevenue = true; + bidResponse.currency = 'USD'; + bidResponse.mediaType = BANNER; + bidResponses.push(bidResponse); + } + }); + return bidResponses; +} + +/* Convert native bid response to compatible format */ +function buildNativeResponse(bidRequest, response) { + const bidResponses = []; + response.seatbid[0].bid.forEach(function (bidderBid) { + let responseCPM; + let placementCode = ''; + + if (bidRequest) { + let bidResponse = createBid(1); + placementCode = bidRequest.placementCode; + bidRequest.status = CONSTANTS.STATUS.GOOD; + responseCPM = parseFloat(bidderBid.price); + if (responseCPM === 0 || isNaN(responseCPM)) { + let bid = createBid(2); + bid.requestId = bidRequest.bidId; + bid.bidderCode = bidRequest.bidder; + bidResponses.push(bid); + return; + } + bidResponse.requestId = bidRequest.bidId + bidResponse.transactionId = bidRequest.transactionId + bidResponse.placementCode = placementCode; + bidResponse.cpm = responseCPM; + + let nativeResponse = JSON.parse(bidderBid.adm).native; + + const native = { + clickUrl: nativeResponse.link.url, + impressionTrackers: nativeResponse.imptrackers + }; + + nativeResponse.assets.forEach(function(asset) { + let keyVal = getNativeAssestObj(asset, bidRequest.assets); + native[keyVal.key] = keyVal.value; + }); + + bidResponse.creativeId = bidderBid.id; + bidResponse.bidderCode = bidRequest.bidder; + bidResponse.ttl = 300; + if (bidRequest.sizes) { bidResponse.size = bidRequest.sizes; } + bidResponse.netRevenue = true; + bidResponse.currency = 'USD'; + bidResponse.native = native; + bidResponse.mediaType = NATIVE; + bidResponses.push(bidResponse); + } + }); + return bidResponses; +} + +/* Convert video bid response to compatible format */ +function buildVideoResponse(bidRequest, response) { + const bidResponses = []; + response.seatbid[0].bid.forEach(function (bidderBid) { + let responseCPM; + let placementCode = ''; + + if (bidRequest) { + let bidResponse = createBid(1); + placementCode = bidRequest.placementCode; + bidRequest.status = CONSTANTS.STATUS.GOOD; + responseCPM = parseFloat(bidderBid.price); + if (responseCPM === 0 || isNaN(responseCPM)) { + let bid = createBid(2); + bid.requestId = bidRequest.bidId; + bid.bidderCode = bidRequest.bidder; + bidResponses.push(bid); + return; + } + let context = bidRequest.mediaTypes.video.context; + + bidResponse.requestId = bidRequest.bidId + bidResponse.transactionId = bidRequest.transactionId + bidResponse.placementCode = placementCode; + bidResponse.cpm = responseCPM; + + let vastXml = decodeURIComponent(bidderBid.adm); + + bidResponse.creativeId = bidderBid.id; + bidResponse.bidderCode = bidRequest.bidder; + bidResponse.ttl = 300; + bidResponse.netRevenue = true; + bidResponse.currency = 'USD'; + var ext = bidderBid.ext; + var vastUrl = ''; + if (ext) { + vastUrl = ext.vast_url; + } + var adUnitCode = bidRequest.adUnitCode; + var sizeObj = getVideoAdUnitSize(bidRequest); + + bidResponse.height = sizeObj.adH; + bidResponse.width = sizeObj.adW; + + switch (context) { + case OUTSTREAM: + var outstreamType = contains(OUTSTREAM_TYPES, bidRequest.params.outstreamType) ? bidRequest.params.outstreamType : '' + bidResponse.outstreamType = outstreamType; + bidResponse.ad = vastXml; + if (!bidRequest.renderer) { + const renderer = Renderer.install({ + id: bidderBid.id, + url: RENDERER_URL, + config: bidRequest.params.outstreamConfig || {}, + loaded: false, + adUnitCode + }); + renderer.setRender(outstreamRender) + bidResponse.renderer = renderer; + } else { bidResponse.adResponse = vastXml; } + break; + case INSTREAM: + bidResponse.vastUrl = vastUrl; + bidResponse.adserverTargeting = setTargeting(vastUrl); + break; + } + bidResponse.mediaType = VIDEO; + bidResponses.push(bidResponse); + } + }); + return bidResponses; +} + +/* Generate renderer for outstream ad unit */ +function outstreamRender(bid) { + bid.renderer.push(() => { + window.osRenderer({ + adResponse: bid.ad, + height: bid.height, + width: bid.width, + targetId: bid.adUnitCode, // target div id to render video + outstreamType: bid.outstreamType, + options: bid.renderer.getConfig(), + }); + }); +} + +/* Set targeting params used for instream video that is required to generate cache url */ +function setTargeting(query) { + var targeting = {}; + var hash; + var hashes = query.slice(query.indexOf('?') + 1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + targeting['hb_' + hash[0]] = hash[1]; + } + return targeting; +} + +/* Get image type with respect to the id */ +function getAssetImageType(id, assets) { + for (var i = 0; i < assets.length; i++) { + if (assets[i].id == id) { + if (assets[i].img.type == 1) { return 'icon'; } else if (assets[i].img.type == 3) { return 'image'; } + } + } + return ''; +} + +/* Get type of data asset with respect to the id */ +function getAssetDataType(id, assets) { + for (var i = 0; i < assets.length; i++) { + if (assets[i].id == id) { + if (assets[i].data.type == 1) { return 'sponsored'; } else if (assets[i].data.type == 2) { return 'desc'; } else if (assets[i].data.type == 12) { return 'cta'; } + } + } + return ''; +} + +/* Convert response assests to compatible format */ +function getNativeAssestObj(obj, assets) { + if (obj.title) { + return { + key: 'title', + value: obj.title.text + } + } + if (obj.data) { + return { + key: getAssetDataType(obj.id, assets), + value: obj.data.value + } + } + if (obj.img) { + return { + key: getAssetImageType(obj.id, assets), + value: { + url: obj.img.url, + height: obj.img.h, + width: obj.img.w + } + } + } +} + +registerBidder(spec); diff --git a/modules/datawrkzBidAdapter.md b/modules/datawrkzBidAdapter.md new file mode 100644 index 00000000000..85ea43e6dd3 --- /dev/null +++ b/modules/datawrkzBidAdapter.md @@ -0,0 +1,160 @@ +# Overview + +``` +Module Name: Datawrkz Bid Adapter +Module Type: Bidder Adapter +Maintainer: pubops@datawrkz.com +``` + +# Description + +Module that connects to Datawrkz's demand sources. +Datawrkz bid adapter supports Banner, Video (instream and outstream) and Native ad units. + +# Bid Parameters + +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `site_id` | required | String | Site id | "test_site_id" +| `deals` | optional | Array | Array of deal objects | `[{id: "deal_1"},{id: "deal_2"}]` +| `bidfloor` | optional | Float | Minimum bid for this impression expressed in CPM | `0.5` +| `outstreamType` | optional | String | Type of outstream video to the played. Available options: inline, slider_top_left, slider_top_right, slider_bottom_left, slider_bottom_right, interstitial_close, and listicle | "inline" +| `outstreamConfig` | optional | Object | Configuration settings for outstream ad unit | `{ad_unit_audio: 1, show_player_close_button_after: 5, hide_player_control: 0}` + +# Deal Object +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `id` | required | String | Deal id | "test_deal_id" + +# outstreamConfig +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `ad_unit_audio` | optional | Integer | Set default audio option for the player. 0 to play audio on hover and 2 to mute | `0` or `2` +| `show_player_close_button_after` | optional | Integer | Show player close button after specified seconds | `5` +| `hide_player_control` | optional | Integer | Show/hide player controls. 0 to show player controls and 1 to hide | `1` + +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'datawrkz', + params: { + site_id: 'site_id', + bidfloor: 0.5 + } + }] + }, + // Native adUnit + { + code: 'native-div', + sizes: [[1, 1]], + mediaTypes: { + native: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [300, 250] + }, + icon: { + required: true, + sizes: [50, 50] + }, + body: { + required: true, + len: 800 + }, + sponsoredBy: { + required: true + }, + cta: { + required: true + } + } + }, + bids: [{ + bidder: 'datawrkz', + params: { + site_id: 'site_id', + bidfloor: 0.5 + } + }] + }, + // Video instream adUnit + { + code: 'video-instream', + sizes: [[640, 480]], + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'instream', + api: [1, 2], + mimes: ["video/x-ms-wmv", "video/mp4"], + protocols: [1, 2, 3], + playbackmethod: [1, 2], + minduration: 20, + maxduration: 30, + startdelay: 5, + minbitrate: 300, + maxbitrate: 1500, + delivery: [2], + linearity: 1 + }, + }, + bids: [{ + bidder: 'datawrkz', + params: { + site_id: 'site_id', + bidfloor: 0.5 + } + }] + }, + // Video outstream adUnit + { + code: 'video-outstream', + sizes: [[300, 250]], + mediaTypes: { + video: { + playerSize: [[300, 250]], + context: 'outstream', + api: [1, 2], + mimes: ["video/mp4"], + protocols: [1, 2, 3], + playbackmethod: [1, 2], + minduration: 20, + maxduration: 30, + startdelay: 5, + minbitrate: 300, + maxbitrate: 1500, + delivery: [2], + linearity: 1 + } + }, + bids: [ + { + bidder: 'datawrkz', + params: { + site_id: 'site_id', + bidfloor: 0.5, + outstreamType: 'slider_top_left', //Supported types : inline, slider_top_left, slider_top_right, slider_bottom_left, slider_bottom_right, interstitial_close, listicle + outstreamConfig: { + ad_unit_audio: 1, // 0: audio on hover, 2: always muted + show_player_close_button_after: 5, // show close button after 5 seconds + hide_player_control: 0 // 0 to show/ 1 to hide + } + } + } + ] + } +]; +``` diff --git a/test/spec/modules/datawrkzBidAdapter_spec.js b/test/spec/modules/datawrkzBidAdapter_spec.js new file mode 100644 index 00000000000..3ddc2bad1cf --- /dev/null +++ b/test/spec/modules/datawrkzBidAdapter_spec.js @@ -0,0 +1,656 @@ +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/datawrkzBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'datawrkz'; +const ENDPOINT_URL = 'https://at.datawrkz.com/exchange/openrtb23/'; +const SITE_ID = 'site_id'; +const FINAL_URL = ENDPOINT_URL + SITE_ID + '?hb=1'; + +describe('datawrkzAdapterTests', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': BIDDER_CODE, + 'params': { + 'site_id': SITE_ID, + 'bidfloor': '1.0' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when params not found', function () { + let bid = Object.assign({}, bid); + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required site_id param not found', function () { + let bid = Object.assign({}, bid); + bid.params = {'bidfloor': '1.0'} + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when adunit is adpod video', function () { + let bid = Object.assign({}, bid); + bid.params = {'bidfloor': '1.0', 'site_id': SITE_ID}; + bid.mediaTypes = { + 'video': { + 'context': 'adpod' + } + } + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const consentString = '1YA-'; + const bidderRequest = { + 'bidderCode': 'datawrkz', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'uspConsent': consentString, + 'gdprConsent': {'gdprApplies': true}, + }; + const bannerBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const bannerBidRequestsSingleArraySlotAndDeals = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, + 'mediaTypes': {'banner': {}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'sizes': [300, 250], + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const nativeBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'native': { + 'title': {'required': true, 'len': 80}, + 'image': {'required': true, 'sizes': [[300, 250]]}, + 'icon': {'required': true, 'sizes': [[50, 50]]}, + 'sponsoredBy': {'required': true}, + 'cta': {'required': true}, + 'body': {'required': true, 'len': 100} + }}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const nativeBidRequestsSingleArraySlotAndDeals = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, + 'mediaTypes': {'native': { + 'title': {'len': 80}, + 'image': {'sizes': [300, 250]}, + 'icon': {'sizes': [50, 50]}, + 'sponsoredBy': {}, + 'cta': {}, + 'body': {'len': 100} + }}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const instreamVideoBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [[640, 480]]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const instreamVideoBidRequestsSingleArraySlotAndDeals = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, + 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [640, 480]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const outstreamVideoBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [[640, 480]], 'mimes': ['video/mp4', 'video/x-flv']}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const outstreamVideoBidRequestsSingleArraySlotAndDeals = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, + 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [640, 480], 'mimes': ['video/mp4', 'video/x-flv']}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const bidRequestsWithNoMediaType = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + + it('empty bid requests', function () { + const requests = spec.buildRequests([], bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('mediaTypes missing in bid request', function () { + const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('invalid media type in bid request', function () { + bidRequestsWithNoMediaType[0].mediaTypes = {'test': {}}; + const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('size missing in bid request for banner', function () { + delete bidRequestsWithNoMediaType[0].mediaTypes.test; + bidRequestsWithNoMediaType[0].mediaTypes.banner = {}; + const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('size array empty in bid request for banner', function () { + bidRequestsWithNoMediaType[0].mediaTypes.banner.sizes = []; + const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('banner bidRequest with slot size as 2 dimensional array', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + const requests = spec.buildRequests(bannerBidRequests, bidderRequest); + config.getConfig.restore(); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload.imp).to.exist; + expect(payload).to.nested.include({'imp[0].banner.w': 300}); + expect(payload).to.nested.include({'imp[0].banner.h': 250}); + expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); + expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); + expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('banner'); + }); + + it('banner bidRequest with deals and slot size as 1 dimensional array', function () { + const requests = spec.buildRequests(bannerBidRequestsSingleArraySlotAndDeals, bidderRequest); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload.imp).to.exist; + expect(payload).to.nested.include({'imp[0].banner.w': 300}); + expect(payload).to.nested.include({'imp[0].banner.h': 250}); + expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); + expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('banner'); + }); + + it('native bidRequest fields with slot size as 2 dimensional array', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + const requests = spec.buildRequests(nativeBidRequests, bidderRequest); + config.getConfig.restore(); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload.imp[0].native.request).to.exist; + expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); + expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); + expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('native'); + }); + + it('native bidRequest fields with deals and slot size as 1 dimensional array', function () { + const requests = spec.buildRequests(nativeBidRequestsSingleArraySlotAndDeals, bidderRequest); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload.imp).to.exist; + expect(payload.imp[0].native.request).to.exist; + expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); + expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('native'); + }); + + it('instream video bidRequest fields with slot size as 2 dimensional array', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + const requests = spec.buildRequests(instreamVideoBidRequests, bidderRequest); + config.getConfig.restore(); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); + expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); + expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); + }); + + it('instream video bidRequest with deals and slot size as 1 dimensional array', function () { + const requests = spec.buildRequests(instreamVideoBidRequestsSingleArraySlotAndDeals, bidderRequest); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); + }); + + it('outstream video bidRequest fields with slot size as 2 dimensional array', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + const requests = spec.buildRequests(outstreamVideoBidRequests, bidderRequest); + config.getConfig.restore(); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload).to.nested.include({'imp[0].video.w': 640}); + expect(payload).to.nested.include({'imp[0].video.h': 480}); + expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); + expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); + expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); + }); + + it('outstream video bidRequest fields with deals and slot size as 1 dimensional array', function () { + const requests = spec.buildRequests(outstreamVideoBidRequestsSingleArraySlotAndDeals, bidderRequest); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload).to.nested.include({'imp[0].video.w': 640}); + expect(payload).to.nested.include({'imp[0].video.h': 480}); + expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); + expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); + }); + }); + + describe('interpretResponse', function () { + const bidRequest = { + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + 'requestedMediaType': 'banner' + }; + const nativeBidRequest = { + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'native': { + 'title': {'required': true, 'len': 80}, + 'image': {'required': true, 'sizes': [300, 250]}, + 'icon': {'required': true, 'sizes': [50, 50]}, + 'sponsoredBy': {'required': true}, + 'cta': {'required': true}, + 'body': {'required': true} + }}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + 'requestedMediaType': 'native', + 'assets': [ + {'id': 1, 'required': 1, 'title': {'len': 80}}, + {'id': 2, 'required': 1, 'img': {'type': 3, 'w': 300, 'h': 250}}, + {'id': 3, 'required': 1, 'img': {'type': 1, 'w': 50, 'h': 50}}, + {'id': 4, 'required': 1, 'data': {'type': 1}}, + {'id': 5, 'required': 1, 'data': {'type': 12}}, + {'id': 6, 'required': 1, 'data': {'type': 2, 'len': 100}} + ] + }; + const instreamVideoBidRequest = { + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [640, 480]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + 'requestedMediaType': 'video' + }; + const outstreamVideoBidRequest = { + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, + 'bidfloor': 1.00, + 'outstreamType': 'slider_top_left', + 'outstreamConfig': + {'ad_unit_audio': 1, 'show_player_close_button_after': 5, 'hide_player_control': 0}}, + 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [640, 480]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + 'requestedMediaType': 'video' + }; + const request = { + 'method': 'POST', + 'url': FINAL_URL, + 'data': '', + bidRequest + }; + + it('check empty response', function () { + const result = spec.interpretResponse({}, request); + expect(result).to.deep.equal([]); + }); + + it('check if id missing in response', function () { + const serverResponse = {'body': {'seatbid': [{}]}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.deep.equal([]); + }); + + it('check if seatbid present in response', function () { + const serverResponse = {'body': {'id': 'id'}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.deep.equal([]); + }); + + it('check empty array response seatbid', function () { + const serverResponse = {'body': {'id': 'id', 'seatbid': []}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.deep.equal([]); + }); + + it('check bid present in seatbid', function () { + const serverResponse = {'body': {'id': 'id', 'seatbid': [{}]}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(0); + }); + + it('check empty array bid in seatbid', function () { + const serverResponse = {'body': {'id': 'id', 'seatbid': [{'bid': []}]}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(0); + }); + + it('banner response missing bid price', function () { + const serverResponse = {'body': {'id': 'id', 'seatbid': [{'bid': [{'id': 1}]}]}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + }); + + it('banner response', function () { + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'price': 1, + 'w': 300, + 'h': 250, + 'adm': 'test adm', + 'nurl': 'url' + } + ] + } + ] + }, + 'headers': {} + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].cpm).to.equal(1); + expect(result[0].size).to.equal(bidRequest.mediaTypes.banner.sizes); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].ad).to.equal(decodeURIComponent(serverResponse.body.seatbid[0].bid[0].adm + '')); + expect(result[0].creativeId).to.equal('1'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); + expect(result[0].mediaType).to.equal('banner'); + }); + + it('native response missing bid price', function () { + request.bidRequest = nativeBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'w': 300, + 'h': 250, + 'adm': '{"native": {"link": {"url": "test_url"}, "imptrackers": [], "assets": [' + + '{"id": 1, "title": {"text": "Test title"}},' + + '{"id": 2, "img": {"type": 3,"url": "https://test/image", "w": 300, "h": 250}},' + + '{"id": 3, "img": {"type": 1, "url": "https://test/icon", "w": 50, "h": 50}},' + + '{"id": 4, "data": {"type": 1, "value": "Test sponsored by"}},' + + '{"id": 5, "data": {"type": 12, "value": "Test CTA"}},' + + '{"id": 6, "data": {"type": 2, "value": "Test body"}}]}}' + } + ] + } + ] + }, + 'headers': {} + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + }); + + it('native response', function () { + request.bidRequest = nativeBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'price': 1, + 'w': 300, + 'h': 250, + 'adm': '{"native": {"link": {"url": "test_url"}, "imptrackers": ["tracker1", "tracker2"], "assets": [' + + '{"id": 1, "title": {"text": "Test title"}},' + + '{"id": 2, "img": {"type": 3,"url": "https://test/image", "w": 300, "h": 250}},' + + '{"id": 3, "img": {"type": 1, "url": "https://test/icon", "w": 50, "h": 50}},' + + '{"id": 4, "data": {"type": 1, "value": "Test sponsored by"}},' + + '{"id": 5, "data": {"type": 12, "value": "Test CTA"}},' + + '{"id": 6, "data": {"type": 2, "value": "Test body"}}]}}' + } + ] + } + ] + }, + 'headers': {} + }; + + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].cpm).to.equal(1); + expect(result[0].native.clickUrl).to.equal('test_url'); + expect(result[0].native.impressionTrackers).to.have.lengthOf(2); + expect(result[0].native.title).to.equal('Test title'); + expect(result[0].native.image.url).to.equal('https://test/image'); + expect(result[0].native.icon.url).to.equal('https://test/icon'); + expect(result[0].native.sponsored).to.equal('Test sponsored by'); + expect(result[0].native.cta).to.equal('Test CTA'); + expect(result[0].native.desc).to.equal('Test body'); + expect(result[0].creativeId).to.equal('1'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); + expect(result[0].mediaType).to.equal('native'); + }); + + it('video response missing bid price', function () { + request.bidRequest = instreamVideoBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'w': 640, + 'h': 480, + 'adm': '', + 'ext': { + 'vast_url': 'vast_url' + } + } + ] + } + ] + }, + 'headers': {} + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + }); + + it('instream video response', function () { + request.bidRequest = instreamVideoBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'price': 1, + 'w': 640, + 'h': 480, + 'adm': '', + 'ext': { + 'vast_url': 'test_vast_url?kcid=123&kaid=12&protocol=3' + } + } + ] + } + ] + }, + 'headers': {} + }; + + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(640); + expect(result[0].height).to.equal(480); + expect(result[0].vastUrl).to.equal('test_vast_url?kcid=123&kaid=12&protocol=3'); + expect(result[0].adserverTargeting.hb_kcid).to.equal('123'); + expect(result[0].adserverTargeting.hb_kaid).to.equal('12'); + expect(result[0].adserverTargeting.hb_protocol).to.equal('3'); + expect(result[0].creativeId).to.equal('1'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); + expect(result[0].mediaType).to.equal('video'); + }); + + it('outstream video response', function () { + request.bidRequest = outstreamVideoBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'price': 1, + 'w': 640, + 'h': 480, + 'adm': '', + 'ext': { + 'vast_url': 'vast_url' + } + } + ] + } + ] + }, + 'headers': {} + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(640); + expect(result[0].height).to.equal(480); + expect(result[0].outstreamType).to.equal('slider_top_left'); + expect(result[0].ad).to.equal(''); + expect(result[0].renderer).to.exist; + expect(result[0].creativeId).to.equal('1'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); + expect(result[0].mediaType).to.equal('video'); + }); + }); +}); From 91ce750022daedff8bc70f5fee94088d7a6cda18 Mon Sep 17 00:00:00 2001 From: Saar Amrani <89377180+saar120@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:21:24 +0300 Subject: [PATCH 149/569] Vidazoo Bid Adapter: Cookie sync improvements (#8834) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * feat(module): changed userSync url and passes cids from responses to usersync. * feat(module): fixed failing test. Co-authored-by: Udi Talias Co-authored-by: roman --- modules/vidazooBidAdapter.js | 10 ++-- test/spec/modules/vidazooBidAdapter_spec.js | 59 +++++++++++++-------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 39d37d55a91..41f3f1f2888 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,4 +1,4 @@ -import { _each, deepAccess, parseSizesInput, parseUrl } from '../src/utils.js'; +import {_each, deepAccess, parseSizesInput, parseUrl, uniques} from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -191,17 +191,19 @@ function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') let syncs = []; const { iframeEnabled, pixelEnabled } = syncOptions; const { gdprApplies, consentString = '' } = gdprConsent; - const params = `?gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` + + const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); + const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` if (iframeEnabled) { syncs.push({ type: 'iframe', - url: `https://prebid.cootlogix.com/api/sync/iframe/${params}` + url: `https://sync.cootlogix.com/api/sync/iframe/${params}` }); } if (pixelEnabled) { syncs.push({ type: 'image', - url: `https://prebid.cootlogix.com/api/sync/image/${params}` + url: `https://sync.cootlogix.com/api/sync/image/${params}` }); } return syncs; diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 1b4ed4b170d..d30b9f31417 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import {expect} from 'chai'; import { spec as adapter, SUPPORTED_ID_SYSTEMS, @@ -15,8 +15,8 @@ import { getVidazooSessionId, } from 'modules/vidazooBidAdapter.js'; import * as utils from 'src/utils.js'; -import { version } from 'package.json'; -import { useFakeTimers } from 'sinon'; +import {version} from 'package.json'; +import {useFakeTimers} from 'sinon'; const SUB_DOMAIN = 'openrtb'; @@ -55,6 +55,7 @@ const BIDDER_REQUEST = { const SERVER_RESPONSE = { body: { + cid: 'testcid123', results: [{ 'ad': '', 'price': 0.8, @@ -84,7 +85,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); + const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); return parsedUrl.search; } catch (e) { return ''; @@ -196,19 +197,27 @@ describe('VidazooBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', - url: 'https://prebid.cootlogix.com/api/sync/iframe/?gdpr=0&gdpr_consent=&us_privacy=' + url: 'https://sync.cootlogix.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' + }]); + }); + + it('should have valid user sync with cid on response', function () { + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.cootlogix.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' }]); }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ - 'url': 'https://prebid.cootlogix.com/api/sync/image/?gdpr=0&gdpr_consent=&us_privacy=', + 'url': 'https://sync.cootlogix.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', 'type': 'image' }]); }) @@ -221,12 +230,12 @@ describe('VidazooBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({ price: 1, ad: '' }); + const responses = adapter.interpretResponse({price: 1, ad: ''}); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); + const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); expect(responses).to.be.empty; }); @@ -265,10 +274,14 @@ describe('VidazooBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { - case 'lipb': return { lipbid: id }; - case 'parrableId': return { eid: id }; - case 'id5id': return { uid: id }; - default: return id; + case 'lipb': + return {lipbid: id}; + case 'parrableId': + return {eid: id}; + case 'id5id': + return {uid: id}; + default: + return id; } })(); @@ -285,18 +298,18 @@ describe('VidazooBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({ 'c_id': '1' }); - const pid = extractPID({ 'p_id': '1' }); - const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); + const cid = extractCID({'c_id': '1'}); + const pid = extractPID({'p_id': '1'}); + const subDomain = extractSubDomain({'sub_domain': 'prebid'}); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({ 'cID': '1' }); - const pid = extractPID({ 'Pid': '2' }); - const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); + const cid = extractCID({'cID': '1'}); + const pid = extractPID({'Pid': '2'}); + const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -409,7 +422,7 @@ describe('VidazooBidAdapter', function () { now }); setStorageItem('myKey', 2020); - const { value, created } = getStorageItem('myKey'); + const {value, created} = getStorageItem('myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -425,8 +438,8 @@ describe('VidazooBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({ event: 'send' }); - const { event } = tryParseJSON(data); + const data = JSON.stringify({event: 'send'}); + const {event} = tryParseJSON(data); expect(event).to.be.equal('send'); }); From 749afff9191a49fc33710e7e95514ecadcf3fcd6 Mon Sep 17 00:00:00 2001 From: Jason Piros Date: Wed, 17 Aug 2022 07:05:23 -0700 Subject: [PATCH 150/569] consumableBidAdapter - add video support (#8793) --- modules/consumableBidAdapter.js | 22 ++- .../spec/modules/consumableBidAdapter_spec.js | 139 ++++++++++++++++++ 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index f6c470d82a9..6d502b24e81 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -1,6 +1,7 @@ import { logWarn, createTrackPixelHtml, deepAccess, isArray, deepSetValue } from '../src/utils.js'; import {config} from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'consumable'; @@ -11,6 +12,7 @@ let bidder = 'consumable'; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. @@ -82,6 +84,10 @@ export const spec = { adTypes: bid.adTypes || getSize(sizes) }, bid.params); + if (bid.mediaTypes.video && bid.mediaTypes.video.playerSize) { + placement.video = bid.mediaTypes.video; + } + if (placement.networkId && placement.siteId && placement.unitId && placement.unitName) { data.placements.push(placement); } @@ -155,6 +161,13 @@ export const spec = { if (decision.mediaType) { bid.meta.mediaType = decision.mediaType; + + if (decision.mediaType === 'video') { + bid.mediaType = 'video'; + bid.vastUrl = decision.vastUrl || undefined; + bid.vastXml = decision.vastXml || undefined; + bid.videoCacheKey = decision.uuid || undefined; + } } bidResponses.push(bid); @@ -240,8 +253,13 @@ function getSize(sizes) { } function retrieveAd(decision, unitId, unitName) { - let ad = decision.contents && decision.contents[0] && decision.contents[0].body + createTrackPixelHtml(decision.impressionUrl); - + let ad; + if (decision.contents && decision.contents[0]) { + ad = decision.contents[0].body + createTrackPixelHtml(decision.impressionUrl); + } + if (decision.vastXml) { + ad = decision.vastXml; + } return ad; } diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index 4e860cb771d..f71cf26b3f9 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -129,6 +129,53 @@ const BIDDER_REQUEST_2 = { } }; +const BIDDER_REQUEST_VIDEO = { + bidderCode: 'consumable', + auctionId: 'a4713c32-3762-4798-b342-4ab810ca770d', + bidderRequestId: '109f2a181342a9', + bidRequest: [ + { + bidder: 'consumable', + params: { + networkId: 9969, + siteId: 730181, + unitId: 123456, + unitName: 'cnsmbl-unit' + }, + placementCode: 'div-gpt-ad-1487778092495-0', + mediaTypes: { + video: { + playerSize: [188, 106], + context: 'instream', + mimes: ['application/javascript', 'application/x-mpegurl', 'video/3gpp', 'video/mp4', 'video/mpeg', 'video/ogg', 'video/webm', 'video/x-m4v', 'video/x-ms-asf', 'video/x-ms-wmv', 'video/x-msvideo'], + minduration: 0, + maxduration: 120, + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + api: [1, 2], + linearity: 1 + } + }, + bidId: '6202d555b2f94537', + bidderRequestId: '109f2a181342a9', + auctionId: 'a4713c32-3762-4798-b342-4ab810ca770d' + } + ], + gdprConsent: { + consentString: 'consent-test', + gdprApplies: true + }, + refererInfo: { + referer: 'http://example.com/page.html', + reachedTop: true, + numIframes: 2, + stack: [ + 'http://example.com/page.html', + 'http://example.com/iframe1.html', + 'http://example.com/iframe2.html' + ] + } +}; + const BIDDER_REQUEST_EMPTY = { bidderCode: 'consumable', auctionId: 'b06458ef-4fe5-4a0b-a61b-bccbcedb7b11', @@ -259,6 +306,58 @@ const AD_SERVER_RESPONSE_2 = { } }; +const AD_SERVER_RESPONSE_VIDEO_1 = { + 'headers': null, + 'body': { + 'user': { 'key': 'ue1-2d33e91b71e74929b4aeecc23f4376f1' }, + 'pixels': [{ 'type': 'image', 'url': '//sync.serverbid.com/ss/' }], + 'decisions': { + '6202d555b2f94537': { + 'adId': 3866158402, + 'creativeId': 'C1-somo-test-video', + 'width': 640, + 'height': 480, + 'pricing': { + 'clearPrice': 1.58 + }, + 'vastUrl': 'https://x.serverbid.com/rtb/v?auc=217c051d06b011ed9cbc72b17f01ec03&sc=1.575&s=22&a=9dcab16d340d664310c2135a76989fe946a9d46e5d5f24ff5e2f17bffbb7704a43638bd3f600951e&n=9&r=0&t=1658158906595', + 'uuid': 'f1e7287514ce11ed9c1de2b3ba87449a', + 'bidderName': 'consumable', + 'adomain': ['consumabletv.com'], + 'cats': ['IAB3-1'], + 'mediaType': 'video', + 'networkId': 1 + } + } + } +}; + +const AD_SERVER_RESPONSE_VIDEO_2 = { + 'headers': null, + 'body': { + 'user': { 'key': 'ue1-2d33e91b71e74929b4aeecc23f4376f1' }, + 'pixels': [{ 'type': 'image', 'url': '//sync.serverbid.com/ss/' }], + 'decisions': { + '6202d555b2f94537': { + 'adId': 3866158402, + 'creativeId': 'C1-somo-test-video', + 'width': 640, + 'height': 480, + 'pricing': { + 'clearPrice': 1.58 + }, + 'vastXml': '', + 'uuid': 'f1e7287514ce11ed9c1de2b3ba87449a', + 'bidderName': 'consumable', + 'adomain': ['consumabletv.com'], + 'cats': ['IAB3-1'], + 'mediaType': 'video', + 'networkId': 1 + } + } + } +}; + const BUILD_REQUESTS_OUTPUT = { method: 'POST', url: 'https://e.serverbid.com/api/v2', @@ -267,6 +366,14 @@ const BUILD_REQUESTS_OUTPUT = { bidderRequest: BIDDER_REQUEST_2 }; +const BUILD_REQUESTS_VIDEO_OUTPUT = { + method: 'POST', + url: 'https://e.serverbid.com/api/v2', + data: '', + bidRequest: BIDDER_REQUEST_VIDEO.bidRequest, + bidderRequest: BIDDER_REQUEST_VIDEO +}; + describe('Consumable BidAdapter', function () { let adapter = spec; @@ -382,6 +489,13 @@ describe('Consumable BidAdapter', function () { expect(data.coppa).to.be.undefined; }); + + it('should contain video object for video requests', function () { + let request = spec.buildRequests(BIDDER_REQUEST_VIDEO.bidRequest, BIDDER_REQUEST_VIDEO); + let data = JSON.parse(request.data); + + expect(data.placements[0].video).to.deep.equal(BIDDER_REQUEST_VIDEO.bidRequest[0].mediaTypes.video); + }); }); describe('interpretResponse validation', function () { it('response should have valid bidderCode', function () { @@ -421,6 +535,31 @@ describe('Consumable BidAdapter', function () { }); }); + it('registers video bids with vastUrl', function () { + let bids = spec.interpretResponse(AD_SERVER_RESPONSE_VIDEO_1, BUILD_REQUESTS_VIDEO_OUTPUT); + + bids.forEach(b => { + expect(b.mediaType).to.equal('video'); + expect(b.meta).to.have.property('mediaType', 'video'); + expect(b.vastUrl).to.equal('https://x.serverbid.com/rtb/v?auc=217c051d06b011ed9cbc72b17f01ec03&sc=1.575&s=22&a=9dcab16d340d664310c2135a76989fe946a9d46e5d5f24ff5e2f17bffbb7704a43638bd3f600951e&n=9&r=0&t=1658158906595'); + expect(b.vastXml).to.be.undefined; + expect(b.videoCacheKey).to.equal('f1e7287514ce11ed9c1de2b3ba87449a'); + }); + }) + + it('registers video bids with vastXml', function () { + let bids = spec.interpretResponse(AD_SERVER_RESPONSE_VIDEO_2, BUILD_REQUESTS_VIDEO_OUTPUT); + + bids.forEach(b => { + expect(b.mediaType).to.equal('video'); + expect(b.meta).to.have.property('mediaType', 'video'); + expect(b.vastXml).to.equal(''); + expect(b.vastUrl).to.be.undefined; + expect(b.ad).to.equal(''); + expect(b.videoCacheKey).to.equal('f1e7287514ce11ed9c1de2b3ba87449a'); + }); + }) + it('handles nobid responses', function () { let EMPTY_RESP = Object.assign({}, AD_SERVER_RESPONSE, {'body': {'decisions': null}}) let bids = spec.interpretResponse(EMPTY_RESP, BUILD_REQUESTS_OUTPUT); From 5e983cde34630acf35e354a8f25ebbbcbf5018d4 Mon Sep 17 00:00:00 2001 From: Vic R <103455651+victorlassomarketing@users.noreply.github.com> Date: Wed, 17 Aug 2022 08:20:41 -0700 Subject: [PATCH 151/569] Lasso Bid Adapter: update version and request credential setting (#8848) * update Lasso adapter request setting * update empty ad response logic --- modules/lassoBidAdapter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lassoBidAdapter.js b/modules/lassoBidAdapter.js index b2f3ccf9a7c..da48bc460c7 100644 --- a/modules/lassoBidAdapter.js +++ b/modules/lassoBidAdapter.js @@ -47,7 +47,7 @@ export const spec = { params: JSON.stringify(bidRequest.params), crumbs: JSON.stringify(bidRequest.crumbs), prebidVersion: '$prebid.version$', - version: 1, + version: 2, coppa: config.getConfig('coppa') == true ? 1 : 0, ccpa: bidderRequest.uspConsent || undefined } @@ -57,7 +57,7 @@ export const spec = { url: getBidRequestUrl(aimXR), data: payload, options: { - withCredentials: false + withCredentials: true }, }; }); @@ -67,7 +67,7 @@ export const spec = { const response = serverResponse && serverResponse.body; const bidResponses = []; - if (!response) { + if (!response || !response.bid.ad) { return bidResponses; } From 984b5cfa7752da9282620f17ecbcd7e2b3283631 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 17 Aug 2022 08:21:44 -0700 Subject: [PATCH 152/569] Prebid core: fix bug with some native assets being lost from ortb native responses (#8785) * Prebid core: fix bug with some native assets being lost from ortb native responses * Do not throw if native response contains assets that were not requested --- src/native.js | 109 +++++++++++++++++++-------------------- test/spec/native_spec.js | 96 ++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 57 deletions(-) diff --git a/src/native.js b/src/native.js index bd20aa5d399..470ac0b9a32 100644 --- a/src/native.js +++ b/src/native.js @@ -366,34 +366,9 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) { return keyValues; } -/** - * Constructs a message object containing asset values for each of the - * requested data keys. - */ -export function getAssetMessage(data, adObject) { - const message = { - message: 'assetResponse', - adId: data.adId, - assets: [], - }; - - if (adObject.native.hasOwnProperty('adTemplate')) { - message.adTemplate = getAssetValue(adObject.native['adTemplate']); - } if (adObject.native.hasOwnProperty('rendererUrl')) { - message.rendererUrl = getAssetValue(adObject.native['rendererUrl']); - } - - data.assets.forEach(asset => { - const key = getKeyByValue(CONSTANTS.NATIVE_KEYS, asset); - const value = getAssetValue(adObject.native[key]); - - message.assets.push({ key, value }); - }); - - return message; -} +const getNativeRequest = (bidResponse) => auctionManager.index.getAdUnit(bidResponse)?.nativeOrtbRequest; -export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse) => auctionManager.index.getAdUnit(bidResponse).nativeOrtbRequest} = {}) { +function assetsMessage(data, adObject, keys, {getNativeReq = getNativeRequest} = {}) { const message = { message: 'assetResponse', adId: data.adId, @@ -401,12 +376,12 @@ export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse // Pass to Prebid Universal Creative all assets, the legacy ones + the ortb ones (under ortb property) const ortbRequest = getNativeReq(adObject); - let nativeReq = adObject.native; + let nativeResp = adObject.native; const ortbResponse = adObject.native?.ortb; let legacyResponse = {}; if (ortbRequest && ortbResponse) { legacyResponse = toLegacyResponse(ortbResponse, ortbRequest); - nativeReq = { + nativeResp = { ...adObject.native, ...legacyResponse }; @@ -416,20 +391,20 @@ export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse } message.assets = []; - Object.keys(nativeReq).forEach(function(key) { - if (key === 'adTemplate' && nativeReq[key]) { - message.adTemplate = getAssetValue(nativeReq[key]); - } else if (key === 'rendererUrl' && nativeReq[key]) { - message.rendererUrl = getAssetValue(nativeReq[key]); + (keys == null ? Object.keys(nativeResp) : keys).forEach(function(key) { + if (key === 'adTemplate' && nativeResp[key]) { + message.adTemplate = getAssetValue(nativeResp[key]); + } else if (key === 'rendererUrl' && nativeResp[key]) { + message.rendererUrl = getAssetValue(nativeResp[key]); } else if (key === 'ext') { - Object.keys(nativeReq[key]).forEach(extKey => { - if (nativeReq[key][extKey]) { - const value = getAssetValue(nativeReq[key][extKey]); + Object.keys(nativeResp[key]).forEach(extKey => { + if (nativeResp[key][extKey]) { + const value = getAssetValue(nativeResp[key][extKey]); message.assets.push({ key: extKey, value }); } }) - } else if (nativeReq[key] && CONSTANTS.NATIVE_KEYS.hasOwnProperty(key)) { - const value = getAssetValue(nativeReq[key]); + } else if (nativeResp[key] && CONSTANTS.NATIVE_KEYS.hasOwnProperty(key)) { + const value = getAssetValue(nativeResp[key]); message.assets.push({ key, value }); } @@ -437,6 +412,19 @@ export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse return message; } +/** + * Constructs a message object containing asset values for each of the + * requested data keys. + */ +export function getAssetMessage(data, adObject, {getNativeReq = getNativeRequest} = {}) { + const keys = data.assets.map((k) => getKeyByValue(CONSTANTS.NATIVE_KEYS, k)); + return assetsMessage(data, adObject, keys, {getNativeReq}); +} + +export function getAllAssetsMessage(data, adObject, {getNativeReq = getNativeRequest} = {}) { + return assetsMessage(data, adObject, null, {getNativeReq}); +} + /** * Native assets can be a string or an object with a url prop. Returns the value * appropriate for sending in adserver targeting or placeholder replacement. @@ -689,40 +677,47 @@ export function legacyPropertiesToOrtbNative(legacyNative) { } export function toOrtbNativeResponse(legacyResponse, ortbRequest) { - // copy the request, so we don't pollute it with response data below - ortbRequest = deepClone(ortbRequest); - const ortbResponse = { ...legacyPropertiesToOrtbNative(legacyResponse), assets: [] }; + + function useRequestAsset(predicate, fn) { + let asset = ortbRequest.assets.find(predicate); + if (asset != null) { + asset = deepClone(asset); + fn(asset); + ortbResponse.assets.push(asset); + } + } + Object.keys(legacyResponse).filter(key => !!legacyResponse[key]).forEach(key => { const value = legacyResponse[key]; switch (key) { // process titles case 'title': - const titleAsset = ortbRequest.assets.find(asset => asset.title != null); - titleAsset.title = { - text: value - }; - ortbResponse.assets.push(titleAsset); + useRequestAsset(asset => asset.title != null, titleAsset => { + titleAsset.title = { + text: value + }; + }) break; case 'image': case 'icon': const imageType = key === 'image' ? NATIVE_IMAGE_TYPES.MAIN : NATIVE_IMAGE_TYPES.ICON; - const imageAsset = ortbRequest.assets.find(asset => asset.img != null && asset.img.type == imageType); - imageAsset.img = { - url: value - }; - ortbResponse.assets.push(imageAsset); + useRequestAsset(asset => asset.img != null && asset.img.type === imageType, imageAsset => { + imageAsset.img = { + url: value + }; + }) break; default: if (key in PREBID_NATIVE_DATA_KEYS_TO_ORTB) { - const dataAsset = ortbRequest.assets.find(asset => asset.data != null && asset.data.type === NATIVE_ASSET_TYPES[PREBID_NATIVE_DATA_KEYS_TO_ORTB[key]]); - dataAsset.data = { - value - }; - ortbResponse.assets.push(dataAsset); + useRequestAsset(asset => asset.data != null && asset.data.type === NATIVE_ASSET_TYPES[PREBID_NATIVE_DATA_KEYS_TO_ORTB[key]], dataAsset => { + dataAsset.data = { + value + }; + }) } break; } diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index a6f8e7ae891..19f417bd88f 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -400,6 +400,66 @@ describe('native.js', function () { value: bid.native.ext.foo, }); }); + + const SAMPLE_ORTB_REQUEST = toOrtbNativeRequest({ + title: 'vtitle', + body: 'vbody' + }); + const SAMPLE_ORTB_RESPONSE = { + native: { + ortb: { + link: { + url: 'url' + }, + assets: [ + { + id: 0, + title: { + text: 'vtitle' + } + }, + { + id: 1, + data: { + value: 'vbody' + } + } + ] + } + } + } + describe('getAllAssetsMessage', () => { + it('returns assets in legacy format for ortb responses', () => { + const actual = getAllAssetsMessage({}, SAMPLE_ORTB_RESPONSE, {getNativeReq: () => SAMPLE_ORTB_REQUEST}); + expect(actual.assets).to.eql([ + { + key: 'clickUrl', + value: 'url' + }, + { + key: 'title', + value: 'vtitle' + }, + { + key: 'body', + value: 'vbody' + }, + ]) + }); + }); + describe('getAssetsMessage', () => { + Object.entries({ + 'hb_native_title': {key: 'title', value: 'vtitle'}, + 'hb_native_body': {key: 'body', value: 'vbody'} + }).forEach(([tkey, assetVal]) => { + it(`returns ${tkey} asset in legacy format for ortb responses`, () => { + const actual = getAssetMessage({ + assets: [tkey] + }, SAMPLE_ORTB_RESPONSE, {getNativeReq: () => SAMPLE_ORTB_REQUEST}) + expect(actual.assets).to.eql([assetVal]) + }) + }) + }) }); describe('validate native openRTB', function () { @@ -1022,3 +1082,39 @@ describe('fireClickTrackers', () => { urls.forEach(url => sinon.assert.calledWith(fetchURL, url)); }) }) + +describe('toOrtbNativeResponse', () => { + it('should work when there are unrequested assets in the response', () => { + const legacyResponse = { + 'title': 'vtitle', + 'body': 'vbody' + } + const request = toOrtbNativeRequest({ + title: { + required: 'true' + }, + + }); + const ortbResponse = toOrtbNativeResponse(legacyResponse, request); + expect(ortbResponse.assets.length).to.eql(1); + }); + + it('should not modify the request', () => { + const legacyResponse = { + title: 'vtitle' + } + const request = toOrtbNativeRequest({ + title: { + required: true + } + }); + const requestCopy = JSON.parse(JSON.stringify(request)); + const response = toOrtbNativeResponse(legacyResponse, request); + expect(request).to.eql(requestCopy); + sinon.assert.match(response.assets[0], { + title: { + text: 'vtitle' + } + }) + }) +}) From a4fdf7d98d7d0b1522d1fa3b1fbf47d2c5230e93 Mon Sep 17 00:00:00 2001 From: omerBrowsi <54346241+omerBrowsi@users.noreply.github.com> Date: Wed, 17 Aug 2022 20:45:03 +0300 Subject: [PATCH 153/569] Browsi RTD Module: add support for page view billable events (#8829) * real time data module, browsi sub module for real time data, new hook bidsBackCallback, fix for config unsubscribe * change timeout&primary ad server only to auctionDelay update docs * support multiple providers * change promise to callbacks configure submodule on submodules.json * bug fixes * use Prebid ajax * tests fix * browsi real time data provider improvements * real time data module, browsi sub module for real time data, new hook bidsBackCallback, fix for config unsubscribe * change timeout&primary ad server only to auctionDelay update docs * support multiple providers * change promise to callbacks configure submodule on submodules.json * bug fixes * use Prebid ajax * tests fix * browsi real time data provider improvements * Browsi RTD provider - add billiable event type * Browsi RTD provider - add billiable event type * lint fixes --- modules/browsiRtdProvider.js | 34 ++++++++++++----- test/spec/modules/browsiRtdProvider_spec.js | 41 ++++++++++++++++++++- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index ffc946cbb33..d39e500e4f9 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -57,6 +57,16 @@ export function addBrowsiTag(data) { return script; } +export function sendPageviewEvent(eventType) { + if (eventType === 'PAGEVIEW') { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: 'browsi', + type: 'pageview', + billingId: generateUUID() + }) + } +} + /** * collect required data from page * send data to browsi server to get predictions @@ -262,10 +272,11 @@ function getPredictionsFromServer(url) { try { const data = JSON.parse(response); if (data && data.p && data.kn) { - setData({p: data.p, kn: data.kn, pmd: data.pmd}); + setData({p: data.p, kn: data.kn, pmd: data.pmd, bet: data.bet}); } else { setData({}); } + sendPageviewEvent(data.bet); addBrowsiTag(data); } catch (err) { logError('unable to parse data'); @@ -336,19 +347,22 @@ export const browsiSubmodule = { function getTargetingData(uc, c, us, a) { const targetingData = getRTD(uc); - const auctionId = a.auctionId + const auctionId = a.auctionId; + const sendAdRequestEvent = _browsiData['bet'] === 'AD_REQUEST'; uc.forEach(auc => { if (isNumber(_ic[auc])) { _ic[auc] = _ic[auc] + 1; } - const transactionId = a.adUnits.find(adUnit => adUnit.code === auc).transactionId; - events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { - vendor: 'browsi', - type: 'adRequest', - billingId: generateUUID(), - transactionId: transactionId, - auctionId: auctionId - }) + if (sendAdRequestEvent) { + const transactionId = a.adUnits.find(adUnit => adUnit.code === auc).transactionId; + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: 'browsi', + type: 'adRequest', + billingId: generateUUID(), + transactionId: transactionId, + auctionId: auctionId + }) + } }); logInfo('Browsi RTD provider returned targeting data', targetingData, 'for', uc) return targetingData; diff --git a/test/spec/modules/browsiRtdProvider_spec.js b/test/spec/modules/browsiRtdProvider_spec.js index c36b48c5105..19483047827 100644 --- a/test/spec/modules/browsiRtdProvider_spec.js +++ b/test/spec/modules/browsiRtdProvider_spec.js @@ -3,6 +3,7 @@ import {makeSlot} from '../integration/faker/googletag.js'; import * as utils from '../../../src/utils' import * as events from '../../../src/events'; import * as sinon from 'sinon'; +import {sendPageviewEvent} from '../../../modules/browsiRtdProvider.js'; describe('browsi Real time data sub module', function () { const conf = { @@ -160,7 +161,19 @@ describe('browsi Real time data sub module', function () { }) }) - describe('should emit billable event', function () { + describe('should emit ad request billable event', function () { + before(() => { + const data = { + p: { + 'adUnit1': {ps: {0: 0.234}}, + 'adUnit2': {ps: {0: 0.134}}}, + kn: 'bv', + pmd: undefined, + bet: 'AD_REQUEST' + }; + browsiRTD.setData(data); + }) + beforeEach(() => { eventsEmitSpy.resetHistory(); }) @@ -232,4 +245,30 @@ describe('browsi Real time data sub module', function () { expect(callArguments).to.eql(expectedCall); }) }) + + describe('should emit pageveiw billable event', function () { + beforeEach(() => { + eventsEmitSpy.resetHistory(); + }) + it('should send event if type is correct', function () { + sendPageviewEvent('PAGEVIEW') + + const expectedCall = { + vendor: 'browsi', + type: 'pageview', + } + + expect(eventsEmitSpy.callCount).to.equal(1); + const callArguments = eventsEmitSpy.getCalls()[0].args[1]; + // billing id is random, we can't check its value + delete callArguments['billingId']; + expect(callArguments).to.eql(expectedCall); + }) + it('should not send event if type is incorrect', function () { + sendPageviewEvent('AD_REQUEST'); + sendPageviewEvent('INACTIVE'); + sendPageviewEvent(undefined); + expect(eventsEmitSpy.callCount).to.equal(0); + }) + }) }); From 2fd7e0f5b88892ac0116050d4c386a621dacf64d Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 17 Aug 2022 11:02:09 -0700 Subject: [PATCH 154/569] Readme: remove dead dev dependency and maintenance badges (#8849) * Readme: remove dead dev dependency badge and issue * Remove maintainability and add back issues --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 11dc01e8f07..bbc0d79ab41 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ [![Build Status](https://circleci.com/gh/prebid/Prebid.js.svg?style=svg)](https://circleci.com/gh/prebid/Prebid.js) [![Percentage of issues still open](http://isitmaintained.com/badge/open/prebid/Prebid.js.svg)](http://isitmaintained.com/project/prebid/Prebid.js "Percentage of issues still open") -[![Code Climate](https://codeclimate.com/github/prebid/Prebid.js/badges/gpa.svg)](https://codeclimate.com/github/prebid/Prebid.js) [![Coverage Status](https://coveralls.io/repos/github/prebid/Prebid.js/badge.svg)](https://coveralls.io/github/prebid/Prebid.js) -[![devDependencies Status](https://david-dm.org/prebid/Prebid.js/dev-status.svg)](https://david-dm.org/prebid/Prebid.js?type=dev) [![Total Alerts](https://img.shields.io/lgtm/alerts/g/prebid/Prebid.js.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/prebid/Prebid.js/alerts/) # Prebid.js From c5f33d36c691448dcba967a172f8dc75b4285082 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 17 Aug 2022 19:56:47 +0000 Subject: [PATCH 155/569] Prebid 7.11.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 749fafaebc4..01d037dee63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.10.0-pre", + "version": "7.11.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 58898eb0110..2b0a7215765 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.11.0-pre", + "version": "7.11.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 059b7c5cbf25806f09af9dc21604260b53507d7e Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 17 Aug 2022 19:56:48 +0000 Subject: [PATCH 156/569] Increment version to 7.12.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01d037dee63..a6ae1e5a3e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.11.0", + "version": "7.12.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 2b0a7215765..a9a4c120c48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.11.0", + "version": "7.12.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 94d34d1783fd6712613247d6696b8cc2ca9579a6 Mon Sep 17 00:00:00 2001 From: m-oranskaya <99481039+m-oranskaya@users.noreply.github.com> Date: Thu, 18 Aug 2022 20:13:17 +1100 Subject: [PATCH 157/569] Adriver Bid and Id Modules: buyerid bug fix (#8768) * initial commit * adriver id submodule add * add id system tests, fix adriver bid adapter tests * adriver: fix buyerid * remarks fixing * removal of excess * delete custom parameter * bug fixes --- modules/adriverBidAdapter.js | 17 +++++------------ modules/adriverIdSystem.js | 3 ++- test/spec/modules/adriverBidAdapter_spec.js | 19 ++++++------------- test/spec/modules/adriverIdSystem_spec.js | 11 +++-------- 4 files changed, 16 insertions(+), 34 deletions(-) diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js index e95f83d2c7b..b19c8318754 100644 --- a/modules/adriverBidAdapter.js +++ b/modules/adriverBidAdapter.js @@ -1,6 +1,6 @@ // ADRIVER BID ADAPTER for Prebid 1.13 import { logInfo, getWindowLocation, getBidIdParameter, _each } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adriver'; @@ -22,8 +22,6 @@ export const spec = { }, buildRequests: function (validBidRequests, bidderRequest) { - logInfo('validBidRequests', validBidRequests); - let win = getWindowLocation(); let customID = Math.round(Math.random() * 999999999) + '-' + Math.round(new Date() / 1000) + '-1-46-'; let siteId = getBidIdParameter('siteid', validBidRequests[0].params) + ''; @@ -99,22 +97,17 @@ export const spec = { }); }); - let userid = validBidRequests[0].userId; - let adrcidCookie = storage.getDataFromLocalStorage('adrcid') || validBidRequests[0].userId.adrcid; - + let adrcidCookie = storage.getDataFromLocalStorage('adrcid') || validBidRequests[0].userId?.adrcid; if (adrcidCookie) { - payload.adrcid = adrcidCookie; - payload.id5 = userid.id5id; - payload.sharedid = userid.pubcid; - payload.unifiedid = userid.tdid; + payload.user.buyerid = adrcidCookie; } const payloadString = JSON.stringify(payload); return { method: 'POST', url: ADRIVER_BID_URL, - data: payloadString, - }; + data: payloadString + } }, interpretResponse: function (serverResponse, bidRequest) { diff --git a/modules/adriverIdSystem.js b/modules/adriverIdSystem.js index 6a492fac508..fb8ce99ec16 100644 --- a/modules/adriverIdSystem.js +++ b/modules/adriverIdSystem.js @@ -73,7 +73,8 @@ export const adriverIdSubmodule = { callback(); } }; - ajax(url, callbacks, undefined, {method: 'GET'}); + let newUrl = url + '&cid=' + (storage.getDataFromLocalStorage('adrcid') || storage.getCookie('adrcid')); + ajax(newUrl, callbacks, undefined, {method: 'GET'}); } }; return {callback: resp}; diff --git a/test/spec/modules/adriverBidAdapter_spec.js b/test/spec/modules/adriverBidAdapter_spec.js index 12c0a15fb06..33084877c14 100644 --- a/test/spec/modules/adriverBidAdapter_spec.js +++ b/test/spec/modules/adriverBidAdapter_spec.js @@ -297,25 +297,18 @@ describe('adriverAdapter', function () { { adrcid: undefined } ] cookieValues.forEach(cookieValue => describe('test cookie exist or not behavior', function () { - let expectedValues = { - adrcid: cookieValue.adrcid, - at: '', - cur: '', - tmax: '', - site: '', - id: '', - user: '', - device: '', - imp: '' - } + let expectedValues = [ + 'buyerid', + 'ext' + ] it('check adrcid if it exists', function () { bidRequests[0].userId.adrcid = cookieValue.adrcid; const payload = JSON.parse(spec.buildRequests(bidRequests).data); if (cookieValue.adrcid) { - expect(Object.keys(payload)).to.have.members(Object.keys(expectedValues)); + expect(Object.keys(payload.user)).to.have.members(expectedValues); } else { - expect(payload.adrcid).to.equal(undefined); + expect(payload.user.buyerid).to.equal(0); } }); })); diff --git a/test/spec/modules/adriverIdSystem_spec.js b/test/spec/modules/adriverIdSystem_spec.js index 29d965d5ed4..bc7d3f191d2 100644 --- a/test/spec/modules/adriverIdSystem_spec.js +++ b/test/spec/modules/adriverIdSystem_spec.js @@ -32,7 +32,6 @@ describe('AdriverIdSystem', function () { expect(request.url).to.include('https://ad.adriver.ru/cgi-bin/json.cgi'); request.respond(503, null, 'Unavailable'); expect(logErrorStub.calledOnce).to.be.true; - expect(callbackSpy.calledOnce).to.be.true; }); it('test call user sync url with the right params', function() { @@ -67,16 +66,12 @@ describe('AdriverIdSystem', function () { let request = server.requests[0]; request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ adrcid: response.adrcid })); - let expectedExpiration = new Date(); - expectedExpiration.setTime(expectedExpiration.getTime() + 86400 * 1825 * 1000); + let now = new Date(); + now.setTime(now.getTime() + 86400 * 1825 * 1000); const minimalDate = new Date(0).toString(); - function dateStringFor(date, maxDeltaMs = 2000) { - return sinon.match((val) => Math.abs(date.getTime() - new Date(val).getTime()) <= maxDeltaMs) - } - if (response.adrcid) { - expect(setCookieStub.calledWith('adrcid', response.adrcid, dateStringFor(expectedExpiration))).to.be.true; + expect(setCookieStub.calledWith('adrcid', response.adrcid, now.toUTCString())).to.be.true; expect(setLocalStorageStub.calledWith('adrcid', response.adrcid)).to.be.true; } else { expect(setCookieStub.calledWith('adrcid', '', minimalDate)).to.be.false; From 940339a3ebb9d425af261ab1535d32b3717c49ac Mon Sep 17 00:00:00 2001 From: Love Sharma Date: Thu, 18 Aug 2022 09:36:21 -0400 Subject: [PATCH 158/569] handle native response privacy link (#8838) Co-authored-by: Zicong Zhou --- src/native.js | 1 + test/spec/native_spec.js | 144 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/src/native.js b/src/native.js index 470ac0b9a32..0e48470d300 100644 --- a/src/native.js +++ b/src/native.js @@ -735,6 +735,7 @@ function toLegacyResponse(ortbResponse, ortbRequest) { const legacyResponse = {}; const requestAssets = ortbRequest?.assets || []; legacyResponse.clickUrl = ortbResponse.link.url; + legacyResponse.privacyLink = ortbResponse.privacy; for (const asset of ortbResponse?.assets || []) { const requestAsset = requestAssets.find(reqAsset => asset.id === reqAsset.id); if (asset.title) { diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index 19f417bd88f..08491edeb64 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -44,6 +44,106 @@ const bid = { }, }; +const ortbBid = { + adId: '123', + transactionId: 'au', + native: { + ortb: { + assets: [ + { + id: 0, + title: { + text: 'Native Creative' + } + }, + { + id: 1, + data: { + value: 'Cool description great stuff' + } + }, + { + id: 2, + data: { + value: 'Do it' + } + }, + { + id: 3, + img: { + url: 'http://cdn.example.com/p/creative-image/image.png', + h: 83, + w: 127 + } + }, + { + id: 4, + img: { + url: 'http://cdn.example.com/p/creative-image/icon.jpg', + h: 742, + w: 989 + } + }, + { + id: 5, + data: { + value: 'AppNexus', + type: 1 + } + } + ], + link: { + url: 'https://www.link.example' + }, + privacy: 'https://privacy-link.example', + ver: '1.2' + } + }, +}; + +const ortbRequest = { + assets: [ + { + id: 0, + required: 0, + title: { + len: 140 + } + }, { + id: 1, + required: 0, + data: { + type: 2 + } + }, { + id: 2, + required: 0, + data: { + type: 12 + } + }, { + id: 3, + required: 0, + img: { + type: 3 + } + }, { + id: 4, + required: 0, + img: { + type: 1 + } + }, { + id: 5, + required: 0, + data: { + type: 1 + } + } + ], + ver: '1.2' +} + const bidWithUndefinedFields = { transactionId: 'au', native: { @@ -401,6 +501,50 @@ describe('native.js', function () { }); }); + it('creates native all asset message with OpenRTB format', function () { + const messageRequest = { + message: 'Prebid Native', + action: 'allAssetRequest', + adId: '123', + }; + + const message = getAllAssetsMessage(messageRequest, ortbBid, {getNativeReq: () => ortbRequest}); + + expect(message.assets.length).to.equal(8); + expect(message.assets).to.deep.include({ + key: 'body', + value: bid.native.body, + }); + expect(message.assets).to.deep.include({ + key: 'image', + value: bid.native.image.url, + }); + expect(message.assets).to.deep.include({ + key: 'clickUrl', + value: bid.native.clickUrl, + }); + expect(message.assets).to.deep.include({ + key: 'title', + value: bid.native.title, + }); + expect(message.assets).to.deep.include({ + key: 'icon', + value: bid.native.icon.url, + }); + expect(message.assets).to.deep.include({ + key: 'cta', + value: bid.native.cta, + }); + expect(message.assets).to.deep.include({ + key: 'sponsoredBy', + value: bid.native.sponsoredBy, + }); + expect(message.assets).to.deep.include({ + key: 'privacyLink', + value: ortbBid.native.ortb.privacy, + }); + }); + const SAMPLE_ORTB_REQUEST = toOrtbNativeRequest({ title: 'vtitle', body: 'vbody' From 8ef12d1f21b3866f2b47c2a65c22ed49b767496d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9onard=20Labat?= Date: Thu, 18 Aug 2022 15:49:58 +0200 Subject: [PATCH 159/569] Criteo Bid Adapter - Add support for banner+native multiformat ad unit (#8842) Previously, the use of a native adunit was exclusive with the banner type. --- modules/criteoBidAdapter.js | 32 +++++++----- test/spec/modules/criteoBidAdapter_spec.js | 61 +++++++++++++--------- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 1c269b2bba4..98aa58f5a70 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -469,15 +469,20 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (bidRequest.params.publisherSubId) { slot.publishersubid = bidRequest.params.publisherSubId; } - if (bidRequest.params.nativeCallback || deepAccess(bidRequest, `mediaTypes.${NATIVE}`)) { + + if (bidRequest.params.nativeCallback || hasNativeMediaType(bidRequest)) { slot.native = true; if (!checkNativeSendId(bidRequest)) { logWarn(LOG_PREFIX + 'all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)'); } - slot.sizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'), parseNativeSize); - } else { + } + + if (hasBannerMediaType(bidRequest)) { slot.sizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'), parseSize); + } else { + slot.sizes = []; } + if (hasVideoMediaType(bidRequest)) { const video = { playersizes: parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize'), parseSize), @@ -554,17 +559,18 @@ function parseSize(size) { return size[0] + 'x' + size[1]; } -function parseNativeSize(size) { - if (size[0] === undefined && size[1] === undefined) { - return '2x2'; - } - return size[0] + 'x' + size[1]; -} - function hasVideoMediaType(bidRequest) { return deepAccess(bidRequest, 'mediaTypes.video') !== undefined; } +function hasBannerMediaType(bidRequest) { + return deepAccess(bidRequest, 'mediaTypes.banner') !== undefined; +} + +function hasNativeMediaType(bidRequest) { + return deepAccess(bidRequest, 'mediaTypes.native') !== undefined; +} + function hasValidVideoMediaType(bidRequest) { let isValid = true; @@ -646,18 +652,18 @@ function enrichSlotWithFloors(slot, bidRequest) { if (bidRequest.mediaTypes?.banner) { slotFloors.banner = {}; const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')) - bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({size: bannerSize, mediaType: BANNER})); + bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({ size: bannerSize, mediaType: BANNER })); } if (bidRequest.mediaTypes?.video) { slotFloors.video = {}; const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) - videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({size: videoSize, mediaType: VIDEO})); + videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({ size: videoSize, mediaType: VIDEO })); } if (bidRequest.mediaTypes?.native) { slotFloors.native = {}; - slotFloors.native['*'] = bidRequest.getFloor({size: '*', mediaType: NATIVE}); + slotFloors.native['*'] = bidRequest.getFloor({ size: '*', mediaType: NATIVE }); } if (Object.keys(slotFloors).length > 0) { diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 7252232676f..7af5f5d77a2 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -13,7 +13,7 @@ import * as utils from 'src/utils.js'; import * as refererDetection from 'src/refererDetection.js'; import { config } from '../../../src/config.js'; import * as storageManager from 'src/storageManager.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; describe('The Criteo bidding adapter', function () { let utilsMock, sandbox; @@ -707,7 +707,7 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.slots[0].sizes[0]).to.equal('undefinedxundefined'); }); - it('should properly detect and get sizes of native sizeless banner', function () { + it('should properly detect and forward native flag', function () { const bidRequests = [ { mediaTypes: { @@ -722,11 +722,10 @@ describe('The Criteo bidding adapter', function () { ]; const request = spec.buildRequests(bidRequests, bidderRequest); const ortbRequest = request.data; - expect(ortbRequest.slots[0].sizes).to.have.lengthOf(1); - expect(ortbRequest.slots[0].sizes[0]).to.equal('2x2'); + expect(ortbRequest.slots[0].native).to.equal(true); }); - it('should properly detect and get size of native sizeless banner', function () { + it('should properly detect and forward native flag', function () { const bidRequests = [ { mediaTypes: { @@ -741,8 +740,7 @@ describe('The Criteo bidding adapter', function () { ]; const request = spec.buildRequests(bidRequests, bidderRequest); const ortbRequest = request.data; - expect(ortbRequest.slots[0].sizes).to.have.lengthOf(1); - expect(ortbRequest.slots[0].sizes[0]).to.equal('2x2'); + expect(ortbRequest.slots[0].native).to.equal(true); }); it('should properly build a networkId request', function () { @@ -1258,11 +1256,13 @@ describe('The Criteo bidding adapter', function () { if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { return { currency: 'USD', - floor: 1.0}; + floor: 1.0 + }; } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { return { currency: 'USD', - floor: 2.0}; + floor: 2.0 + }; } else { return {} } @@ -1273,9 +1273,10 @@ describe('The Criteo bidding adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.slots[0].ext.floors).to.deep.equal({ 'banner': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} - }}); + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + }); }); it('should properly build a video request with several player sizes with floors', function () { @@ -1297,11 +1298,13 @@ describe('The Criteo bidding adapter', function () { if (inputParams.mediaType === VIDEO && inputParams.size[0] === 300 && inputParams.size[1] === 250) { return { currency: 'USD', - floor: 1.0}; + floor: 1.0 + }; } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 728 && inputParams.size[1] === 90) { return { currency: 'USD', - floor: 2.0}; + floor: 2.0 + }; } else { return {} } @@ -1312,9 +1315,10 @@ describe('The Criteo bidding adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.slots[0].ext.floors).to.deep.equal({ 'video': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} - }}); + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + }); }); it('should properly build a multi format request with floors', function () { @@ -1347,19 +1351,23 @@ describe('The Criteo bidding adapter', function () { if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { return { currency: 'USD', - floor: 1.0}; + floor: 1.0 + }; } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { return { currency: 'USD', - floor: 2.0}; + floor: 2.0 + }; } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 640 && inputParams.size[1] === 480) { return { currency: 'EUR', - floor: 3.2}; + floor: 3.2 + }; } else if (inputParams.mediaType === NATIVE && inputParams.size === '*') { return { currency: 'YEN', - floor: 4.99}; + floor: 4.99 + }; } else { return {} } @@ -1371,15 +1379,16 @@ describe('The Criteo bidding adapter', function () { expect(request.data.slots[0].ext.data.someContextAttribute).to.deep.equal('abc'); expect(request.data.slots[0].ext.floors).to.deep.equal({ 'banner': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } }, 'video': { - '640x480': {'currency': 'EUR', 'floor': 3.2} + '640x480': { 'currency': 'EUR', 'floor': 3.2 } }, 'native': { - '*': {'currency': 'YEN', 'floor': 4.99} - }}); + '*': { 'currency': 'YEN', 'floor': 4.99 } + } + }); }); }); From 442931dd2f257826db611725120e0ee748423d74 Mon Sep 17 00:00:00 2001 From: wsusrasp <106743463+wsusrasp@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:51:46 +0200 Subject: [PATCH 160/569] Ras Bid Adapter: support for SlotSequence parameter (#8792) * add rasbidadapter pos param * Read pos off the adunit * rename conflicting pos parameter for clarity --- modules/rasBidAdapter.js | 8 ++++++++ modules/rasBidAdapter.md | 27 +++++++++++++------------ test/spec/modules/rasBidAdapter_spec.js | 2 ++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/modules/rasBidAdapter.js b/modules/rasBidAdapter.js index 909b6a7b795..7bc3cf66b0d 100644 --- a/modules/rasBidAdapter.js +++ b/modules/rasBidAdapter.js @@ -1,3 +1,4 @@ +import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { isEmpty, getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; @@ -92,11 +93,18 @@ const getSlots = (bidRequests) => { const batchSize = bidRequests.length; for (let i = 0; i < batchSize; i++) { const adunit = bidRequests[i]; + const slotSequence = utils.deepAccess(adunit, 'params.slotSequence'); + const sizes = parseSizesInput(getAdUnitSizes(adunit)).join(','); + queryString += `&slot${i}=${encodeURIComponent(adunit.params.slot)}&id${i}=${encodeURIComponent(adunit.bidId)}&composition${i}=CHILD`; + if (sizes.length) { queryString += `&iusizes${i}=${encodeURIComponent(sizes)}`; } + if (slotSequence !== undefined) { + queryString += `&pos${i}=${encodeURIComponent(slotSequence)}`; + } } return queryString; }; diff --git a/modules/rasBidAdapter.md b/modules/rasBidAdapter.md index 5cf75c3446d..384ba0b611f 100644 --- a/modules/rasBidAdapter.md +++ b/modules/rasBidAdapter.md @@ -34,17 +34,18 @@ var adUnits = [{ # Parameters -| Name | Scope | Type | Description | Example -| --- | --- | --- | --- | --- -| network | required | String | Specific identifier provided by RAS | `"4178463"` -| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by RAS | `"example_com"` -| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` -| slot | required | String | Ad unit placement name (case-insensitive) provided by RAS | `"slot"` -| pageContext | optional | Object | Web page context data | `{}` -| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` -| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` +| Name | Scope | Type | Description | Example +| --- | --- | --- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- +| network | required | String | Specific identifier provided by RAS | `"4178463"` +| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by RAS | `"example_com"` +| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` +| slot | required | String | Ad unit placement name (case-insensitive) provided by RAS | `"slot"` +| slotSequence | optional | Number | Ad unit sequence position provided by RAS | `1` +| pageContext | optional | Object | Web page context data | `{}` +| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` +| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` | pageContext.dv | optional | String | Document virtual address as slash-separated path that may consist of any number of parts (case-insensitive alphanumeric with underscores and hyphens); first part should be the same as `site` value and second as `area` value; next parts may reflect website navigation | `"example_com/sport/football"` -| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` -| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` -| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` -| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` +| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` +| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` +| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` +| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` diff --git a/test/spec/modules/rasBidAdapter_spec.js b/test/spec/modules/rasBidAdapter_spec.js index 324bf782672..8c378aaa416 100644 --- a/test/spec/modules/rasBidAdapter_spec.js +++ b/test/spec/modules/rasBidAdapter_spec.js @@ -58,6 +58,7 @@ describe('rasBidAdapter', function () { slot: 'test', area: 'areatest', site: 'test', + slotSequence: '0', network: '4178463' } }; @@ -140,6 +141,7 @@ describe('rasBidAdapter', function () { expect(requests[0].url).to.have.string('DV=test%2Fareatest'); expect(requests[0].url).to.have.string('kwrd=val1%2Bval2'); expect(requests[0].url).to.have.string('kvadunit=test%2Fareatest'); + expect(requests[0].url).to.have.string('pos0=0'); }); }); From d77309aad8fc00faaddf42da163ace477c41e91e Mon Sep 17 00:00:00 2001 From: Catalin Ciocov Date: Thu, 18 Aug 2022 17:54:53 +0300 Subject: [PATCH 161/569] Improve Digital adapter: refactor code to align with latest RAZR creative tags (#8827) --- modules/improvedigitalBidAdapter.js | 78 ++++++++++++------- src/adloader.js | 3 +- .../modules/improvedigitalBidAdapter_spec.js | 22 +++--- 3 files changed, 61 insertions(+), 42 deletions(-) diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index 4996f0efaf0..c8fc8eb7a2a 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -19,6 +19,7 @@ import {Renderer} from '../src/Renderer.js'; import {createEidsArray} from './userId/eids.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {loadExternalScript} from '../src/adloader.js'; const BIDDER_CODE = 'improvedigital'; const CREATIVE_TTL = 300; @@ -212,7 +213,7 @@ export const spec = { ID_RESPONSE.buildAd(bid, bidRequest, bidObject); - ID_RAZR.addBidData({ + ID_RAZR.forwardBid({ bidRequest, bid }); @@ -640,37 +641,58 @@ const ID_OUTSTREAM = { }; const ID_RAZR = { - RENDERER_URL: 'https://razr.improvedigital.com/renderer.js', - addBidData({bid, bidRequest}) { - if (this.isValidBid(bid)) { - bid.renderer = Renderer.install({ - url: this.RENDERER_URL, - config: {bidRequest} - }); - bid.renderer.setRender(this.render); + RENDERER_URL: 'https://cdn.360yield.com/razr/tag.js', + + forwardBid({bidRequest, bid}) { + if (bid.mediaType !== BANNER) { + return; } - }, - isValidBid(bid) { - return bid && /razr:\/\//.test(bid.ad); + const cfg = { + prebid: { + bidRequest, + bid + } + }; + + const cfgStr = JSON.stringify(cfg).replace(/<\/script>/g, '\\x3C/script>'); + const s = ``; + bid.ad = bid.ad.replace(/]*>/, match => match + s); + + this.installListener(); }, - render(bid) { - const {bidRequest} = bid.renderer.getConfig(); - - const payload = { - type: 'prebid', - bidRequest, - bid, - config: mergeDeep( - {}, - config.getConfig('improvedigital.rendererConfig'), - deepAccess(bidRequest, 'params.rendererConfig') - ) - }; + installListener() { + if (this._listenerInstalled) { + return; + } + + window.addEventListener('message', function(e) { + const data = e.data?.razr?.load; + if (!data) { + return; + } + + if (e.source) { + data.source = e.source; + if (data.id) { + e.source.postMessage({ + razr: { + id: data.id + } + }, '*'); + } + } + + const ns = window.razr = window.razr || {}; + ns.q = ns.q || []; + ns.q.push(data); + + if (!ns.loaded) { + loadExternalScript(ID_RAZR.RENDERER_URL, BIDDER_CODE); + } + }); - const razr = window.razr = window.razr || {}; - razr.queue = razr.queue || []; - razr.queue.push(payload); + this._listenerInstalled = true; } }; diff --git a/src/adloader.js b/src/adloader.js index 1e7995a9dc6..6b7427d3e52 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -18,7 +18,8 @@ const _approvedLoadExternalJSList = [ 'ftrackId', 'inskin', 'hadron', - 'medianet' + 'medianet', + 'improvedigital' ] /** diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index a882f5ca5ef..19e91cbde96 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -1031,7 +1031,7 @@ describe('Improve Digital Adapter Tests', function () { width: 728, height: 90, ttl: 300, - ad: '  ', + ad: '  ', creativeId: '510265', dealId: 320896, netRevenue: false, @@ -1042,6 +1042,12 @@ describe('Improve Digital Adapter Tests', function () { } ]; + const multiFormatExpectedBid = [ + Object.assign({}, expectedBid[0], { + ad: '  ' + }) + ]; + const expectedTwoBids = [ expectedBid[0], { @@ -1051,7 +1057,7 @@ describe('Improve Digital Adapter Tests', function () { width: 300, height: 250, ttl: 300, - ad: '  ', + ad: '  ', creativeId: '479163', dealId: 320896, netRevenue: false, @@ -1091,7 +1097,7 @@ describe('Improve Digital Adapter Tests', function () { it('should return a well-formed display bid for multi-format ad unit', function () { const bids = spec.interpretResponse(serverResponse, {bidderRequest: multiFormatBidderRequest}); - expect(bids).to.deep.equal(expectedBid); + expect(bids).to.deep.equal(multiFormatExpectedBid); }); it('should return two bids', function () { @@ -1233,16 +1239,6 @@ describe('Improve Digital Adapter Tests', function () { bids = spec.interpretResponse(videoResponse, {bidderRequest: multiFormatBidderRequest}); expect(bids[0].mediaType).to.equal(VIDEO); }); - - it('should not affect non-RAZR bids', function () { - const bids = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(bids[0].renderer).to.not.exist; - }); - - it('should detect RAZR bids', function () { - const bids = spec.interpretResponse(serverResponseRazr, {bidderRequest}); - expect(bids[0].renderer).to.exist; - }); }); describe('getUserSyncs', function () { From 4e9ccd5b207f7195bdd420b7ed0af6a29b16fb0d Mon Sep 17 00:00:00 2001 From: Love Sharma Date: Thu, 18 Aug 2022 10:57:33 -0400 Subject: [PATCH 162/569] IX Bid Adapter: Native OpenRTB Request Support (#8853) * fix native click trackers to only fire on click * fix unit tests for ix * remove version for native requests * remove unnecessary request conversion Co-authored-by: Zicong Zhou --- modules/ixBidAdapter.js | 319 +------------------------ test/spec/modules/ixBidAdapter_spec.js | 314 ++++++++++++++++-------- 2 files changed, 223 insertions(+), 410 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index ce4fdcc2431..61e518d306d 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -25,7 +25,6 @@ import {find} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {Renderer} from '../src/Renderer.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'ix'; const ALIAS_BIDDER_CODE = 'roundel'; @@ -100,93 +99,6 @@ const VIDEO_PARAMS_ALLOW_LIST = [ 'delivery', 'pos', 'companionad', 'api', 'companiontype', 'ext', 'playerSize', 'w', 'h' ]; -const NATIVE_ASSET_TYPES = { - TITLE: 100, - IMG: 200, - VIDEO: 300, - DATA: 400 -}; -const NATIVE_IMAGE_TYPES = { - ICON: 1, - MAIN: 3 -}; -const NATIVE_DATA_TYPES = { - SPONSORED: 1, - DESC: 2, - RATING: 3, - LIKES: 4, - DOWNLOADS: 5, - PRICE: 6, - SALEPRICE: 7, - PHONE: 8, - ADDRESS: 9, - DESC2: 10, - DISPLAYURL: 11, - CTATEXT: 12 -}; -const NATIVE_DATA_MAP = { - [NATIVE_DATA_TYPES.SPONSORED]: 'sponsoredBy', - [NATIVE_DATA_TYPES.DESC]: 'body', - [NATIVE_DATA_TYPES.RATING]: 'rating', - [NATIVE_DATA_TYPES.LIKES]: 'likes', - [NATIVE_DATA_TYPES.DOWNLOADS]: 'downloads', - [NATIVE_DATA_TYPES.PRICE]: 'price', - [NATIVE_DATA_TYPES.SALEPRICE]: 'salePrice', - [NATIVE_DATA_TYPES.PHONE]: 'phone', - [NATIVE_DATA_TYPES.ADDRESS]: 'address', - [NATIVE_DATA_TYPES.DESC2]: 'body2', - [NATIVE_DATA_TYPES.DISPLAYURL]: 'displayUrl', - [NATIVE_DATA_TYPES.CTATEXT]: 'cta' -}; -const NATIVE_ASSETS_MAP = { - 'title': { assetType: NATIVE_ASSET_TYPES.TITLE }, - 'icon': { assetType: NATIVE_ASSET_TYPES.IMG, subtype: NATIVE_IMAGE_TYPES.ICON }, - 'image': { assetType: NATIVE_ASSET_TYPES.IMG, subtype: NATIVE_IMAGE_TYPES.MAIN }, - 'sponsoredBy': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.SPONSORED }, - 'body': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.DESC }, - 'rating': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.RATING }, - 'likes': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.LIKES }, - 'downloads': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.DOWNLOADS }, - 'price': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.PRICE }, - 'salePrice': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.SALEPRICE }, - 'phone': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.PHONE }, - 'address': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.ADDRESS }, - 'body2': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.DESC2 }, - 'displayUrl': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.DISPLAYURL }, - 'cta': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.CTATEXT }, - 'video': { assetType: NATIVE_ASSET_TYPES.VIDEO } -}; -const NATIVE_ALLOWED_PROPERTIES = [ - 'rendererUrl', - 'sendTargetingKeys', - 'adTemplate', - 'type', - 'ext', - 'privacyLink', - 'clickUrl', - 'privacyIcon' -]; -const NATIVE_ASSET_DEFAULT = { - TITLE: { - LEN: 25 - }, - VIDEO: { - MIMES: [ - 'video/mp4', - 'video/webm' - ], - MINDURATION: 0, - MAXDURATION: 120, - PROTOCOLS: [2, 3, 5, 6], - } -}; -const NATIVE_EVENT_TYPES = { - IMRESSION: 1 -}; -const NATIVE_EVENT_TRACKING_METHOD = { - IMG: 1, - JS: 2 -}; const LOCAL_STORAGE_KEY = 'ixdiag'; let hasRegisteredHandler = false; export const storage = getStorageManager({gvlid: GLOBAL_VENDOR_ID, bidderCode: BIDDER_CODE}); @@ -307,50 +219,13 @@ function bidToVideoImp(bid) { */ function bidToNativeImp(bid) { const imp = bidToImp(bid); - const nativeAdUnitRef = deepAccess(bid, 'mediaTypes.native'); - - const assets = []; - - // Convert all native assets to imp object - for (const [adUnitProperty, adUnitValues] of Object.entries(nativeAdUnitRef)) { - if (!NATIVE_ASSETS_MAP[adUnitProperty]) { - continue; - } - - const { assetType, subtype } = NATIVE_ASSETS_MAP[adUnitProperty]; - let asset; - switch (assetType) { - case NATIVE_ASSET_TYPES.TITLE: - asset = createNativeTitleRequest(adUnitValues); - break; - case NATIVE_ASSET_TYPES.IMG: - asset = createNativeImgRequest(adUnitValues, subtype); - break; - case NATIVE_ASSET_TYPES.VIDEO: - asset = createNativeVideoRequest(adUnitValues); - break; - case NATIVE_ASSET_TYPES.DATA: - asset = createNativeDataRequest(adUnitValues, subtype); - break; - } - asset.id = assetType + (subtype || 0); - assets.push(asset); - } - - if (assets.length === 0) { - logWarn('IX Bid Adapter: Native bid does not contain recognised assets in [mediaTypes.native]'); - return {}; - } - const request = { - assets: assets, - ver: '1.2', - eventtrackers: [{ - event: 1, - methods: [1, 2] - }], - privacy: 1 - }; + const request = bid.nativeOrtbRequest + request.eventtrackers = [{ + event: 1, + methods: [1, 2] + }]; + request.privacy = 1; imp.native = { request: JSON.stringify(request), @@ -365,80 +240,6 @@ function bidToNativeImp(bid) { return imp; } -/** - * Converts native bid asset to a native impression asset - * @param {object} bidAsset PBJS bid asset object - * @returns {object} IX impression asset object - */ -function createNativeTitleRequest(bidAsset) { - return { - required: bidAsset.required ? 1 : 0, - title: { - len: bidAsset.len ? bidAsset.len : NATIVE_ASSET_DEFAULT.TITLE.LEN, - ext: bidAsset.ext - } - } -} - -/** - * Converts native bid asset to a native impression asset - * @param {object} bidAsset PBJS bid asset object - * @param {int} type The image type - * @returns {object} IX impression asset object - */ -function createNativeImgRequest(bidAsset, type) { - let asset = { - required: bidAsset.required ? 1 : 0, - img: { - type: type, - mimes: bidAsset.mimes, - ext: bidAsset.ext - } - } - - if (bidAsset.hasOwnProperty('sizes') && bidAsset.sizes.length === 2) { - asset.img.wmin = bidAsset.sizes[0]; - asset.img.hmin = bidAsset.sizes[1]; - } - - return asset -} - -/** - * Converts native bid asset to a native impression asset - * @param {object} bidAsset PBJS bid asset object - * @returns {object} IX impression asset object - */ -function createNativeVideoRequest(bidAsset) { - return { - required: bidAsset.required ? 1 : 0, - video: { - mimes: bidAsset.mimes ? bidAsset.mimes : NATIVE_ASSET_DEFAULT.VIDEO.MIMES, - minduration: bidAsset.minduration ? bidAsset.minduration : NATIVE_ASSET_DEFAULT.VIDEO.MINDURATION, - maxduration: bidAsset.maxduration ? bidAsset.maxduration : NATIVE_ASSET_DEFAULT.VIDEO.MAXDURATION, - protocols: bidAsset.protocols ? bidAsset.protocols : NATIVE_ASSET_DEFAULT.VIDEO.PROTOCOLS, - ext: bidAsset.ext - } - } -} - -/** - * Converts native bid asset to a native impression asset - * @param {object} bidAsset PBJS bid asset object - * @param {int} type The image type - * @returns {object} IX impression asset object - */ -function createNativeDataRequest(bidAsset, type) { - return { - required: bidAsset.required ? 1 : 0, - data: { - type: type, - len: bidAsset.len, - ext: bidAsset.ext - } - } -} - /** * Converts an incoming PBJS bid to an IX Impression * @param {object} bid PBJS bid object @@ -559,7 +360,7 @@ function parseBid(rawBid, currency, bidRequest) { bid.mediaTypes = bidRequest.mediaTypes; bid.ttl = isValidExpiry ? rawBid.exp : VIDEO_TIME_TO_LIVE; } else if (parsedAdm && parsedAdm.native) { - bid.native = interpretNativeAdm(parsedAdm.native); + bid.native = {ortb: parsedAdm.native}; bid.width = rawBid.w ? rawBid.w : 1; bid.height = rawBid.h ? rawBid.h : 1; bid.mediaType = NATIVE; @@ -583,84 +384,6 @@ function parseBid(rawBid, currency, bidRequest) { return bid; } -/** - * Parse native adm and set native asset key names recognized by Prebid.js - * @param {string} adm Native adm complience - */ -function interpretNativeAdm(nativeResponse) { - const native = { - clickUrl: nativeResponse.link.url, - privacyLink: nativeResponse.privacy - }; - - for (const asset of nativeResponse.assets) { - const subtype = asset.id % 100; - const assetType = asset.id - subtype; - - switch (assetType) { - case NATIVE_ASSET_TYPES.TITLE: - native.title = asset.title && asset.title.text; - break; - case NATIVE_ASSET_TYPES.IMG: - const image = { - url: asset.img && asset.img.url, - height: asset.img && asset.img.h, - width: asset.img && asset.img.w - }; - native[subtype === NATIVE_IMAGE_TYPES.ICON ? 'icon' : 'image'] = image; - break; - case NATIVE_ASSET_TYPES.VIDEO: - native.video = asset.video && asset.video.vasttag; - break; - case NATIVE_ASSET_TYPES.DATA: - setDataAsset(native, asset, subtype); - break; - default: - logWarn(`IX Bid Adapter: native asset ID ${asset.id} could not be recognized`); - } - } - - setTrackers(native, nativeResponse); - return native; -} - -function setDataAsset(native, asset, type) { - if (!(type in NATIVE_DATA_MAP)) { - logWarn(`IX Bid Adapter: native data asset type ${type} is not supported`); - return; - } - native[NATIVE_DATA_MAP[type]] = asset.data && asset.data.value; -} - -function setTrackers(native, nativeResponse) { - native.impressionTrackers = [] - - if (Array.isArray(nativeResponse.imptrackers)) { - native.impressionTrackers.push(...nativeResponse.imptrackers) - } - - if (Array.isArray(nativeResponse.link.clicktrackers)) { - native.impressionTrackers.push(...nativeResponse.link.clicktrackers) - } - - if (Array.isArray(nativeResponse.eventtrackers)) { - nativeResponse.eventtrackers.forEach(tracker => { - if (tracker.event !== NATIVE_EVENT_TYPES.IMRESSION) { - return - } - - switch (tracker.method) { - case NATIVE_EVENT_TRACKING_METHOD.IMG: - native.impressionTrackers.push(tracker.url); - break; - case NATIVE_EVENT_TRACKING_METHOD.JS: - native.javascriptTrackers = ``; - break; - } - }) - } -} - /** * Determines whether or not the given object is valid size format. * @@ -758,25 +481,13 @@ function isValidBidFloorParams(bidFloor, bidFloorCur) { bidFloorCur.match(curRegex)); } -function nativeMediaTypeValid(nativeObj) { - if (nativeObj === undefined) { - return true; - } - - let hasValidAsset = false; - - for (const property in nativeObj) { - if (!(property in NATIVE_ASSETS_MAP) && !NATIVE_ALLOWED_PROPERTIES.includes(property)) { - logError('IX Bid Adapter: native', { bidder: BIDDER_CODE, code: ERROR_CODES.PROPERTY_NOT_INCLUDED }); - return false; - } - - if (property in NATIVE_ASSETS_MAP) { - hasValidAsset = true; - } +function nativeMediaTypeValid(bid) { + const nativeMediaTypes = deepAccess(bid, 'mediaTypes.native'); + if (nativeMediaTypes === undefined) { + return true } - return hasValidAsset; + return bid.nativeOrtbRequest && Array.isArray(bid.nativeOrtbRequest.assets) && bid.nativeOrtbRequest.assets.length > 0 } /** @@ -837,8 +548,6 @@ function getEidInfo(allEids) { * */ function buildRequest(validBidRequests, bidderRequest, impressions, version) { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); // Always use secure HTTPS protocol. let baseUrl = SECURE_BID_URL; // Get ids from Prebid User ID Modules @@ -966,7 +675,6 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { // Use the siteId in the first bid request as the main siteId. siteID = validBidRequests[0].params.siteId; payload.s = siteID; - payload.v = version; if (version) { payload.v = version; } @@ -1603,7 +1311,6 @@ export const spec = { const paramsSize = deepAccess(bid, 'params.size'); const mediaTypeBannerSizes = deepAccess(bid, 'mediaTypes.banner.sizes'); const mediaTypeVideoRef = deepAccess(bid, 'mediaTypes.video'); - const mediaTypeNativeRef = deepAccess(bid, 'mediaTypes.native'); const mediaTypeVideoPlayerSize = deepAccess(bid, 'mediaTypes.video.playerSize'); const hasBidFloor = bid.params.hasOwnProperty('bidFloor'); const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur'); @@ -1670,7 +1377,7 @@ export const spec = { } } - return nativeMediaTypeValid(mediaTypeNativeRef); + return nativeMediaTypeValid(bid); }, /** diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 243b702f03d..7360024eed2 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -10,7 +10,6 @@ describe('IndexexchangeAdapter', function () { const IX_SECURE_ENDPOINT = 'https://htlb.casalemedia.com/openrtb/pbjs'; const VIDEO_ENDPOINT_VERSION = 8.1; const BANNER_ENDPOINT_VERSION = 7.2; - const NATIVE_ENDPOINT_VERSION = undefined; const SAMPLE_SCHAIN = { 'ver': '1.0', @@ -378,6 +377,7 @@ describe('IndexexchangeAdapter', function () { required: false }, title: { + len: 25, required: true }, body: { @@ -387,13 +387,20 @@ describe('IndexexchangeAdapter', function () { required: true }, video: { - required: false + required: false, + mimes: ['video/mp4', 'video/webm'], + minduration: 0, + maxduration: 120, + protocols: [2, 3, 5, 6] }, sponsoredBy: { required: true } } }, + nativeOrtbRequest: { + assets: [{id: 0, required: 0, img: {type: 1}}, {id: 1, required: 1, title: {len: 140}}, {id: 2, required: 1, data: {type: 2}}, {id: 3, required: 1, img: {type: 3}}, {id: 4, required: false, video: {mimes: ['video/mp4', 'video/webm'], minduration: 0, maxduration: 120, protocols: [2, 3, 5, 6]}}] + }, adUnitCode: 'div-gpt-ad-1460505748563-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47231', bidId: '1a2b3c4f', @@ -432,6 +439,9 @@ describe('IndexexchangeAdapter', function () { } } }, + nativeOrtbRequest: { + assets: [{id: 0, required: 0, img: {type: 1}}, {id: 1, required: 1, title: {len: 140}}, {id: 2, required: 1, data: {type: 2}}, {id: 3, required: 1, img: {type: 3}}, {id: 4, required: false, video: {mimes: ['video/mp4', 'video/webm'], minduration: 0, maxduration: 120, protocols: [2, 3, 5, 6]}}] + }, adUnitCode: 'div-gpt-ad-1460505748562-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', @@ -442,7 +452,7 @@ describe('IndexexchangeAdapter', function () { ]; const DEFAULT_NATIVE_IMP = { - request: '{"assets":[{"required":0,"img":{"type":1},"id":201},{"required":1,"title":{"len":25},"id":100},{"required":1,"data":{"type":2},"id":402},{"required":1,"img":{"type":3},"id":203},{"required":0,"video":{"mimes":["video/mp4","video/webm"],"minduration":0,"maxduration":120,"protocols":[2,3,5,6]},"id":300},{"required":1,"data":{"type":1},"id":401}],"ver":"1.2","eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', + request: '{"assets":[{"id":0,"required":0,"img":{"type":1}},{"id":1,"required":1,"title":{"len":140}},{"id":2,"required":1,"data":{"type":2}},{"id":3,"required":1,"img":{"type":3}},{"id":4,"required":false,"video":{"mimes":["video/mp4","video/webm"],"minduration":0,"maxduration":120,"protocols":[2,3,5,6]}}],"eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2' } @@ -588,7 +598,7 @@ describe('IndexexchangeAdapter', function () { advbrandid: 303325, advbrand: 'OECTA' }, - adm: '{"native":{"ver":"1.2","assets":[{"id":201,"img":{"url":"https://cdn.liftoff.io/customers/1209/creatives/2501-icon-250x250.png","w":250,"h":250}},{"id":203,"img":{"url":"https://cdn.liftoff.io/customers/5a9cab9cc6/image/lambda_png/a0355879b06c09b09232.png","w":1200,"h":627}},{"id":401,"data":{"value":"autodoc.co.uk"}},{"id":402,"data":{"value":"Les pièces automobiles dont vous avez besoin, toujours sous la main."}},{"id":100,"title":{"text":"Autodoc"}},{"id":300,"video":{"vasttag":"blah"}}],"link":{"url":"https://play.google.com/store/apps/details?id=de.autodoc.gmbh","clicktrackers":["https://click.liftoff.io/v1/campaign_click/blah"]},"eventtrackers":[{"event":1,"method":1,"url":"https://impression-europe.liftoff.io/index/impression"},{"event":1,"method":1,"url":"https://a701.casalemedia.com/impression/v1"}],"privacy":"https://privacy.link.com"}}' + adm: '{"native":{"ver":"1.2","assets":[{"id":0,"img":{"url":"https://cdn.liftoff.io/customers/1209/creatives/2501-icon-250x250.png","w":250,"h":250}},{"id":1,"img":{"url":"https://cdn.liftoff.io/customers/5a9cab9cc6/image/lambda_png/a0355879b06c09b09232.png","w":1200,"h":627}},{"id":2,"data":{"value":"autodoc.co.uk"}},{"id":3,"data":{"value":"Les pièces automobiles dont vous avez besoin, toujours sous la main."}},{"id":4,"title":{"text":"Autodoc"}},{"id":5,"video":{"vasttag":"blah"}}],"link":{"url":"https://play.google.com/store/apps/details?id=de.autodoc.gmbh","clicktrackers":["https://click.liftoff.io/v1/campaign_click/blah"]},"eventtrackers":[{"event":1,"method":1,"url":"https://impression-europe.liftoff.io/index/impression"},{"event":1,"method":1,"url":"https://a701.casalemedia.com/impression/v1"}],"privacy":"https://privacy.link.com"}}' } ], seat: '3970' @@ -864,7 +874,7 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return true when required params found for a banner or video ad', function () { + it('should return true when required params found for a banner, video or native ad', function () { expect(spec.isBidRequestValid(DEFAULT_BANNER_VALID_BID[0])).to.equal(true); expect(spec.isBidRequestValid(DEFAULT_VIDEO_VALID_BID[0])).to.equal(true); expect(spec.isBidRequestValid(DEFAULT_NATIVE_VALID_BID[0])).to.equal(true); @@ -1064,15 +1074,12 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should fail when native contains unrecongized properties', function () { + it('should fail if native openRTB object contains no valid assets', function () { let bid = utils.deepClone(DEFAULT_NATIVE_VALID_BID[0]); - bid.mediaTypes.native.test = {} + bid.nativeOrtbRequest = {} expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('should fail if native mediaTypes should contains no valid assets', function () { - let bid = utils.deepClone(DEFAULT_NATIVE_VALID_BID[0]); - bid.mediaTypes.native = {} + bid.nativeOrtbRequest = {assets: []} expect(spec.isBidRequestValid(bid)).to.be.false; }); }); @@ -2145,7 +2152,7 @@ describe('IndexexchangeAdapter', function () { it('should have native request', () => { const nativeImpression = JSON.parse(request[1].data.r).imp[0]; - expect(request[1].data.v).to.equal(NATIVE_ENDPOINT_VERSION); + expect(request[1].data.hasOwnProperty('v')).to.equal(false); expect(nativeImpression.id).to.equal(DEFAULT_NATIVE_VALID_BID[0].bidId); expect(nativeImpression.native).to.deep.equal(DEFAULT_NATIVE_IMP); }); @@ -2491,7 +2498,7 @@ describe('IndexexchangeAdapter', function () { const request = spec.buildRequests(DEFAULT_NATIVE_VALID_BID, DEFAULT_OPTION); const query = request[0].data; - expect(query.v).to.equal(NATIVE_ENDPOINT_VERSION); + expect(query.hasOwnProperty('v')).to.equal(false); expect(query.s).to.equal(DEFAULT_NATIVE_VALID_BID[0].params.siteId); expect(query.r).to.exist; expect(query.ac).to.equal('j'); @@ -2516,92 +2523,148 @@ describe('IndexexchangeAdapter', function () { const request = spec.buildRequests(DEFAULT_NATIVE_VALID_BID, DEFAULT_OPTION); const nativeImpression = JSON.parse(request[0].data.r).imp[0]; - expect(request[0].data.v).to.equal(NATIVE_ENDPOINT_VERSION); + expect(request[0].data.hasOwnProperty('v')).to.equal(false); expect(nativeImpression.id).to.equal(DEFAULT_NATIVE_VALID_BID[0].bidId); expect(nativeImpression.native).to.deep.equal(DEFAULT_NATIVE_IMP); }); it('should build request with given asset properties', function() { let bid = utils.deepClone(DEFAULT_NATIVE_VALID_BID) - bid[0].mediaTypes.native = { - title: { - len: 200 - }, - video: { - mimes: [ - 'javascript' - ], - minduration: 10, - maxduration: 60, - protocols: [1] - } + bid[0].nativeOrtbRequest = { + assets: [{id: 0, required: 0, title: {len: 140}}, {id: 1, required: 0, video: {mimes: ['javascript'], minduration: 10, maxduration: 60, protocols: [1]}}] } const request = spec.buildRequests(bid, DEFAULT_OPTION); const nativeImpression = JSON.parse(request[0].data.r).imp[0]; - expect(nativeImpression.native).to.deep.equal({request: '{"assets":[{"required":0,"title":{"len":200},"id":100},{"required":0,"video":{"mimes":["javascript"],"minduration":10,"maxduration":60,"protocols":[1]},"id":300}],"ver":"1.2","eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2'}); + expect(nativeImpression.native).to.deep.equal({request: '{"assets":[{"id":0,"required":0,"title":{"len":140}},{"id":1,"required":0,"video":{"mimes":["javascript"],"minduration":10,"maxduration":60,"protocols":[1]}}],"eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2'}); }); it('should build request with all possible Prebid asset properties', function() { let bid = utils.deepClone(DEFAULT_NATIVE_VALID_BID) - bid[0].mediaTypes.native = { - title: { - required: false - }, - body: { - required: false - }, - body2: { - required: false - }, - sponsoredBy: { - required: false - }, - icon: { - required: false - }, - image: { - required: false - }, - clickUrl: { - required: false - }, - displayUrl: { - required: false - }, - privacyLink: { - required: false - }, - privacyIcon: { - required: false - }, - cta: { - required: false - }, - rating: { - required: false - }, - downloads: { - required: false - }, - likes: { - required: false - }, - price: { - required: false - }, - salePrice: { - required: false - }, - address: { - required: false - }, - phone: { - required: false - }, + bid[0].nativeOrtbRequest = { + 'ver': '1.2', + 'assets': [ + { + 'id': 0, + 'required': 0, + 'title': { + 'len': 140 + } + }, + { + 'id': 1, + 'required': 0, + 'data': { + 'type': 2 + } + }, + { + 'id': 2, + 'required': 0, + 'data': { + 'type': 10 + } + }, + { + 'id': 3, + 'required': 0, + 'data': { + 'type': 1 + } + }, + { + 'id': 4, + 'required': 0, + 'img': { + 'type': 1 + } + }, + { + 'id': 5, + 'required': 0, + 'img': { + 'type': 3 + } + }, + { + 'id': 6, + 'required': 0 + }, + { + 'id': 7, + 'required': 0, + 'data': { + 'type': 11 + } + }, + { + 'id': 8, + 'required': 0 + }, + { + 'id': 9, + 'required': 0 + }, + { + 'id': 10, + 'required': 0, + 'data': { + 'type': 12 + } + }, + { + 'id': 11, + 'required': 0, + 'data': { + 'type': 3 + } + }, + { + 'id': 12, + 'required': 0, + 'data': { + 'type': 5 + } + }, + { + 'id': 13, + 'required': 0, + 'data': { + 'type': 4 + } + }, + { + 'id': 14, + 'required': 0, + 'data': { + 'type': 6 + } + }, + { + 'id': 15, + 'required': 0, + 'data': { + 'type': 7 + } + }, + { + 'id': 16, + 'required': 0, + 'data': { + 'type': 9 + } + }, + { + 'id': 17, + 'required': 0, + 'data': { + 'type': 8 + } + } + ] } const request = spec.buildRequests(bid, DEFAULT_OPTION); const nativeImpression = JSON.parse(request[0].data.r).imp[0]; - expect(nativeImpression.native).to.deep.equal({request: '{"assets":[{"required":0,"title":{"len":25},"id":100},{"required":0,"data":{"type":2},"id":402},{"required":0,"data":{"type":10},"id":410},{"required":0,"data":{"type":1},"id":401},{"required":0,"img":{"type":1},"id":201},{"required":0,"img":{"type":3},"id":203},{"required":0,"data":{"type":11},"id":411},{"required":0,"data":{"type":12},"id":412},{"required":0,"data":{"type":3},"id":403},{"required":0,"data":{"type":5},"id":405},{"required":0,"data":{"type":4},"id":404},{"required":0,"data":{"type":6},"id":406},{"required":0,"data":{"type":7},"id":407},{"required":0,"data":{"type":9},"id":409},{"required":0,"data":{"type":8},"id":408}],"ver":"1.2","eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2'}); + expect(nativeImpression.native).to.deep.equal({request: '{"ver":"1.2","assets":[{"id":0,"required":0,"title":{"len":140}},{"id":1,"required":0,"data":{"type":2}},{"id":2,"required":0,"data":{"type":10}},{"id":3,"required":0,"data":{"type":1}},{"id":4,"required":0,"img":{"type":1}},{"id":5,"required":0,"img":{"type":3}},{"id":6,"required":0},{"id":7,"required":0,"data":{"type":11}},{"id":8,"required":0},{"id":9,"required":0},{"id":10,"required":0,"data":{"type":12}},{"id":11,"required":0,"data":{"type":3}},{"id":12,"required":0,"data":{"type":5}},{"id":13,"required":0,"data":{"type":4}},{"id":14,"required":0,"data":{"type":6}},{"id":15,"required":0,"data":{"type":7}},{"id":16,"required":0,"data":{"type":9}},{"id":17,"required":0,"data":{"type":8}}],"eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2'}); }) }); @@ -3091,27 +3154,70 @@ describe('IndexexchangeAdapter', function () { advertiserDomains: ['www.abc.com'] }, native: { - body: 'Les pièces automobiles dont vous avez besoin, toujours sous la main.', - clickUrl: 'https://play.google.com/store/apps/details?id=de.autodoc.gmbh', - icon: { - height: 250, - width: 250, - url: 'https://cdn.liftoff.io/customers/1209/creatives/2501-icon-250x250.png' - }, - image: { - height: 627, - width: 1200, - url: 'https://cdn.liftoff.io/customers/5a9cab9cc6/image/lambda_png/a0355879b06c09b09232.png' - }, - impressionTrackers: [ - 'https://click.liftoff.io/v1/campaign_click/blah', - 'https://impression-europe.liftoff.io/index/impression', - 'https://a701.casalemedia.com/impression/v1' - ], - privacyLink: 'https://privacy.link.com', - sponsoredBy: 'autodoc.co.uk', - title: 'Autodoc', - video: 'blah' + ortb: { + assets: [ + { + 'id': 0, + 'img': { + 'h': 250, + 'url': 'https://cdn.liftoff.io/customers/1209/creatives/2501-icon-250x250.png', + 'w': 250 + } + }, + { + 'id': 1, + 'img': { + 'h': 627, + 'url': 'https://cdn.liftoff.io/customers/5a9cab9cc6/image/lambda_png/a0355879b06c09b09232.png', + 'w': 1200 + } + }, + { + 'data': { + 'value': 'autodoc.co.uk' + }, + 'id': 2 + }, + { + 'data': { + 'value': 'Les pièces automobiles dont vous avez besoin, toujours sous la main.' + }, + 'id': 3 + }, + { + 'id': 4, + 'title': { + 'text': 'Autodoc' + } + }, + { + 'id': 5, + 'video': { + 'vasttag': 'blah' + } + } + ], + 'eventtrackers': [ + { + 'event': 1, + 'method': 1, + 'url': 'https://impression-europe.liftoff.io/index/impression' + }, + { + 'event': 1, + 'method': 1, + 'url': 'https://a701.casalemedia.com/impression/v1' + } + ], + 'link': { + 'clicktrackers': [ + 'https://click.liftoff.io/v1/campaign_click/blah' + ], + 'url': 'https://play.google.com/store/apps/details?id=de.autodoc.gmbh' + }, + 'privacy': 'https://privacy.link.com', + 'ver': '1.2' + } }, ttl: 3600 } From 296a081949b11a09b789f17f1b7dba5a72b7d5b3 Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Thu, 18 Aug 2022 13:46:13 -0400 Subject: [PATCH 163/569] Update Sonobi adapter with GVLID (#8860) --- modules/sonobiBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index c6100ac8b40..3c841cc4d8a 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -11,6 +11,7 @@ const OUTSTREAM_REDNERER_URL = 'https://mtrx.go.sonobi.com/sbi_outstream_rendere export const spec = { code: BIDDER_CODE, + gvlid: 104, supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. From ed74e44a6cec85a6eb572d19033b57e2cef7a482 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 18 Aug 2022 11:06:44 -0700 Subject: [PATCH 164/569] dgkeyword RTD provider: fix tests causing ID5 test failures (#8862) --- modules/dgkeywordRtdProvider.js | 9 +++++++++ test/spec/modules/dgkeywordRtdProvider_spec.js | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js index cea33014144..e2a29375f25 100644 --- a/modules/dgkeywordRtdProvider.js +++ b/modules/dgkeywordRtdProvider.js @@ -25,6 +25,15 @@ export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, us const timeout = (moduleConfig && moduleConfig.params && moduleConfig.params.timeout && Number(moduleConfig.params.timeout) > 0) ? Number(moduleConfig.params.timeout) : PROFILE_TIMEOUT_MS; const url = (moduleConfig && moduleConfig.params && moduleConfig.params.url) ? moduleConfig.params.url : URL + encodeURIComponent(window.location.href); const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + callback = (function(cb) { + let done = false; + return function () { + if (!done) { + done = true; + return cb.apply(this, arguments); + } + } + })(callback); let isFinish = false; logMessage('[dgkeyword sub module]', adUnits, timeout); let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js index a145f429557..5ecec48f6b4 100644 --- a/test/spec/modules/dgkeywordRtdProvider_spec.js +++ b/test/spec/modules/dgkeywordRtdProvider_spec.js @@ -293,8 +293,8 @@ describe('Digital Garage Keyword Module', function () { moduleConfig, null ); + const request = server.requests[0]; setTimeout(() => { - const request = server.requests[0]; if (request) { request.respond( 200, From f69cc666f12d03fbb5b0c4342d17f71103dd1d2f Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 18 Aug 2022 19:02:43 -0400 Subject: [PATCH 165/569] Id5 id configurable fetch flow (#8784) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Kowalski --- modules/id5IdSystem.js | 271 ++++++---- modules/id5IdSystem.md | 30 +- test/spec/modules/id5IdSystem_spec.js | 701 +++++++++++++++++++------- 3 files changed, 705 insertions(+), 297 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 96ec1fed754..95ce5c94e46 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -7,19 +7,19 @@ import { deepAccess, - logInfo, deepSetValue, - logError, isEmpty, isEmptyStr, + logError, + logInfo, logWarn, safeJSONParse } from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; -import { submodule } from '../src/hook.js'; -import { getRefererInfo } from '../src/refererDetection.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { uspDataHandler } from '../src/adapterManager.js'; +import {ajax} from '../src/ajax.js'; +import {submodule} from '../src/hook.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {uspDataHandler} from '../src/adapterManager.js'; const MODULE_NAME = 'id5Id'; const GVLID = 131; @@ -28,10 +28,11 @@ export const ID5_STORAGE_NAME = 'id5id'; export const ID5_PRIVACY_STORAGE_NAME = `${ID5_STORAGE_NAME}_privacy`; const LOCAL_STORAGE = 'html5'; const LOG_PREFIX = 'User ID - ID5 submodule: '; +const ID5_API_CONFIG_URL = 'https://id5-sync.com/api/config/prebid' // order the legacy cookie names in reverse priority order so the last // cookie in the array is the most preferred to use -const LEGACY_COOKIE_NAMES = [ 'pbjs-id5id', 'id5id.1st', 'id5id' ]; +const LEGACY_COOKIE_NAMES = ['pbjs-id5id', 'id5id.1st', 'id5id']; export const storage = getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME}); @@ -102,92 +103,27 @@ export const id5IdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function getId - * @param {SubmoduleConfig} config + * @param {SubmoduleConfig} submoduleConfig * @param {ConsentData} consentData * @param {(Object|undefined)} cacheIdObj * @returns {IdResponse|undefined} */ - getId(config, consentData, cacheIdObj) { - if (!hasRequiredConfig(config)) { + getId(submoduleConfig, consentData, cacheIdObj) { + if (!hasRequiredConfig(submoduleConfig)) { return undefined; } - const url = `https://id5-sync.com/g/v2/${config.params.partner}.json`; - const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; - const usp = uspDataHandler.getConsentData(); - const referer = getRefererInfo(); - const signature = (cacheIdObj && cacheIdObj.signature) ? cacheIdObj.signature : getLegacyCookieSignature(); - const data = { - 'partner': config.params.partner, - 'gdpr': hasGdpr, - 'nbPage': incrementNb(config.params.partner), - 'o': 'pbjs', - 'rf': referer.topmostLocation, - 'top': referer.reachedTop ? 1 : 0, - 'u': referer.stack[0] || window.location.href, - 'v': '$prebid.version$' - }; - - // pass in optional data, but only if populated - if (hasGdpr && typeof consentData.consentString !== 'undefined' && !isEmpty(consentData.consentString) && !isEmptyStr(consentData.consentString)) { - data.gdpr_consent = consentData.consentString; - } - if (typeof usp !== 'undefined' && !isEmpty(usp) && !isEmptyStr(usp)) { - data.us_privacy = usp; - } - if (typeof signature !== 'undefined' && !isEmptyStr(signature)) { - data.s = signature; - } - if (typeof config.params.pd !== 'undefined' && !isEmptyStr(config.params.pd)) { - data.pd = config.params.pd; - } - if (typeof config.params.provider !== 'undefined' && !isEmptyStr(config.params.provider)) { - data.provider = config.params.provider; - } - - const abTestingConfig = getAbTestingConfig(config); - if (abTestingConfig.enabled === true) { - data.ab_testing = { - enabled: true, - control_group_pct: abTestingConfig.controlGroupPct // The server validates - }; - } - - const resp = function (callback) { - const callbacks = { - success: response => { - let responseObj; - if (response) { - try { - responseObj = JSON.parse(response); - logInfo(LOG_PREFIX + 'response received from the server', responseObj); - - resetNb(config.params.partner); - - if (responseObj.privacy) { - storeInLocalStorage(ID5_PRIVACY_STORAGE_NAME, JSON.stringify(responseObj.privacy), NB_EXP_DAYS); - } - - // TODO: remove after requiring publishers to use localstorage and - // all publishers have upgraded - if (config.storage.type === LOCAL_STORAGE) { - removeLegacyCookies(config.params.partner); - } - } catch (error) { - logError(LOG_PREFIX + error); - } - } - callback(responseObj); - }, - error: error => { + const resp = function (cbFunction) { + new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData()).execute() + .then(response => { + cbFunction(response) + }) + .catch(error => { logError(LOG_PREFIX + 'getId fetch encountered an error', error); - callback(); - } - }; - logInfo(LOG_PREFIX + 'requesting an ID from the server', data); - ajax(url, callbacks, JSON.stringify(data), { method: 'POST', withCredentials: true }); + cbFunction(); + }); }; - return { callback: resp }; + return {callback: resp}; }, /** @@ -212,6 +148,139 @@ export const id5IdSubmodule = { } }; +class IdFetchFlow { + constructor(submoduleConfig, gdprConsentData, cacheIdObj, usPrivacyData) { + this.submoduleConfig = submoduleConfig + this.gdprConsentData = gdprConsentData + this.cacheIdObj = cacheIdObj + this.usPrivacyData = usPrivacyData + } + + execute() { + return this.#callForConfig(this.submoduleConfig) + .then(fetchFlowConfig => { + return this.#callForExtensions(fetchFlowConfig.extensionsCall) + .then(extensionsData => { + return this.#callId5Fetch(fetchFlowConfig.fetchCall, extensionsData) + }) + }) + .then(fetchCallResponse => { + try { + resetNb(this.submoduleConfig.params.partner); + if (fetchCallResponse.privacy) { + storeInLocalStorage(ID5_PRIVACY_STORAGE_NAME, JSON.stringify(fetchCallResponse.privacy), NB_EXP_DAYS); + } + } catch (error) { + logError(LOG_PREFIX + error); + } + return fetchCallResponse; + }) + } + + #ajaxPromise(url, data, options) { + return new Promise((resolve, reject) => { + ajax(url, + { + success: function (res) { + resolve(res) + }, + error: function (err) { + reject(err) + } + }, data, options) + }) + } + + // eslint-disable-next-line no-dupe-class-members + #callForConfig(submoduleConfig) { + let url = submoduleConfig.params.configUrl || ID5_API_CONFIG_URL; // override for debug/test purposes only + return this.#ajaxPromise(url, JSON.stringify(submoduleConfig), {method: 'POST'}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'config response received from the server', responseObj); + return responseObj; + }); + } + + // eslint-disable-next-line no-dupe-class-members + #callForExtensions(extensionsCallConfig) { + if (extensionsCallConfig === undefined) { + return Promise.resolve(undefined) + } + let extensionsUrl = extensionsCallConfig.url + let method = extensionsCallConfig.method || 'GET' + let data = method === 'GET' ? undefined : JSON.stringify(extensionsCallConfig.body || {}) + return this.#ajaxPromise(extensionsUrl, data, {'method': method}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'extensions response received from the server', responseObj); + return responseObj; + }) + } + + // eslint-disable-next-line no-dupe-class-members + #callId5Fetch(fetchCallConfig, extensionsData) { + let url = fetchCallConfig.url; + let additionalData = fetchCallConfig.overrides || {}; + let data = { + ...this.#createFetchRequestData(), + ...additionalData, + extensions: extensionsData + }; + return this.#ajaxPromise(url, JSON.stringify(data), {method: 'POST', withCredentials: true}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'fetch response received from the server', responseObj); + return responseObj; + }); + } + + // eslint-disable-next-line no-dupe-class-members + #createFetchRequestData() { + const params = this.submoduleConfig.params; + const hasGdpr = (this.gdprConsentData && typeof this.gdprConsentData.gdprApplies === 'boolean' && this.gdprConsentData.gdprApplies) ? 1 : 0; + const referer = getRefererInfo(); + const signature = (this.cacheIdObj && this.cacheIdObj.signature) ? this.cacheIdObj.signature : getLegacyCookieSignature(); + const nbPage = incrementNb(params.partner); + const data = { + 'partner': params.partner, + 'gdpr': hasGdpr, + 'nbPage': nbPage, + 'o': 'pbjs', + 'rf': referer.topmostLocation, + 'top': referer.reachedTop ? 1 : 0, + 'u': referer.stack[0] || window.location.href, + 'v': '$prebid.version$', + 'storage': this.submoduleConfig.storage + }; + + // pass in optional data, but only if populated + if (hasGdpr && this.gdprConsentData.consentString !== undefined && !isEmpty(this.gdprConsentData.consentString) && !isEmptyStr(this.gdprConsentData.consentString)) { + data.gdpr_consent = this.gdprConsentData.consentString; + } + if (this.usPrivacyData !== undefined && !isEmpty(this.usPrivacyData) && !isEmptyStr(this.usPrivacyData)) { + data.us_privacy = this.usPrivacyData; + } + if (signature !== undefined && !isEmptyStr(signature)) { + data.s = signature; + } + if (params.pd !== undefined && !isEmptyStr(params.pd)) { + data.pd = params.pd; + } + if (params.provider !== undefined && !isEmptyStr(params.provider)) { + data.provider = params.provider; + } + const abTestingConfig = params.abTesting || {enabled: false}; + + if (abTestingConfig.enabled) { + data.ab_testing = { + enabled: true, control_group_pct: abTestingConfig.controlGroupPct // The server validates + }; + } + return data + } +} + function hasRequiredConfig(config) { if (!config || !config.params || !config.params.partner || typeof config.params.partner !== 'number') { logError(LOG_PREFIX + 'partner required to be defined as a number'); @@ -242,25 +311,29 @@ export function expDaysStr(expDays) { export function nbCacheName(partnerId) { return `${ID5_STORAGE_NAME}_${partnerId}_nb`; } + export function storeNbInCache(partnerId, nb) { storeInLocalStorage(nbCacheName(partnerId), nb, NB_EXP_DAYS); } + export function getNbFromCache(partnerId) { let cacheNb = getFromLocalStorage(nbCacheName(partnerId)); return (cacheNb) ? parseInt(cacheNb) : 0; } + function incrementNb(partnerId) { const nb = (getNbFromCache(partnerId) + 1); storeNbInCache(partnerId, nb); return nb; } + function resetNb(partnerId) { storeNbInCache(partnerId, 0); } function getLegacyCookieSignature() { let legacyStoredValue; - LEGACY_COOKIE_NAMES.forEach(function(cookie) { + LEGACY_COOKIE_NAMES.forEach(function (cookie) { if (storage.getCookie(cookie)) { legacyStoredValue = safeJSONParse(storage.getCookie(cookie)) || legacyStoredValue; } @@ -268,21 +341,6 @@ function getLegacyCookieSignature() { return (legacyStoredValue && legacyStoredValue.signature) || ''; } -/** - * Remove our legacy cookie values. Needed until we move all publishers - * to html5 storage in a future release - * @param {integer} partnerId - */ -function removeLegacyCookies(partnerId) { - logInfo(LOG_PREFIX + 'removing legacy cookies'); - LEGACY_COOKIE_NAMES.forEach(function(cookie) { - storage.setCookie(`${cookie}`, ' ', expDaysStr(-1)); - storage.setCookie(`${cookie}_nb`, ' ', expDaysStr(-1)); - storage.setCookie(`${cookie}_${partnerId}_nb`, ' ', expDaysStr(-1)); - storage.setCookie(`${cookie}_last`, ' ', expDaysStr(-1)); - }); -} - /** * This will make sure we check for expiration before accessing local storage * @param {string} key @@ -303,6 +361,7 @@ export function getFromLocalStorage(key) { storage.removeDataFromLocalStorage(key); return null; } + /** * Ensure that we always set an expiration in local storage since * by default it's not required @@ -315,14 +374,4 @@ export function storeInLocalStorage(key, value, expDays) { storage.setDataInLocalStorage(`${key}`, value); } -/** - * gets the existing abTesting config or generates a default config with abTesting off - * - * @param {SubmoduleConfig|undefined} config - * @returns {Object} an object which always contains at least the property "enabled" - */ -function getAbTestingConfig(config) { - return deepAccess(config, 'params.abTesting', { enabled: false }); -} - submodule('userId', id5IdSubmodule); diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 11f8ffc5609..cf90290b1d8 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -29,7 +29,8 @@ pbjs.setConfig({ abTesting: { // optional enabled: true, // false by default controlGroupPct: 0.1 // valid values are 0.0 - 1.0 (inclusive) - } + }, + disableExtensions: false // optional }, storage: { type: 'html5', // "html5" is the required storage type @@ -43,21 +44,22 @@ pbjs.setConfig({ }); ``` -| Param under userSync.userIds[] | Scope | Type | Description | Example | +| Param under userSync.userIds[] | Scope | Type | Description | Example | | --- | --- | --- | --- | --- | -| name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | -| params | Required | Object | Details for the ID5 ID. | | -| params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | -| params.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | -| params.provider | Optional | String | An identifier provided by ID5 to technology partners who manage Prebid setups on behalf of publishers. Reach out to [ID5](mailto:prebid@id5.io) if you have questions about this parameter | `pubmatic-identity-hub` | -| params.abTesting | Optional | Object | Allows publishers to easily run an A/B Test. If enabled and the user is in the Control Group, the ID5 ID will NOT be exposed to bid adapters for that request | Disabled by default | -| params.abTesting.enabled | Optional | Boolean | Set this to `true` to turn on this feature | `true` or `false` | +| name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | +| params | Required | Object | Details for the ID5 ID. | | +| params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | +| params.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | +| params.provider | Optional | String | An identifier provided by ID5 to technology partners who manage Prebid setups on behalf of publishers. Reach out to [ID5](mailto:prebid@id5.io) if you have questions about this parameter | `pubmatic-identity-hub` | +| params.abTesting | Optional | Object | Allows publishers to easily run an A/B Test. If enabled and the user is in the Control Group, the ID5 ID will NOT be exposed to bid adapters for that request | Disabled by default | +| params.abTesting.enabled | Optional | Boolean | Set this to `true` to turn on this feature | `true` or `false` | | params.abTesting.controlGroupPct | Optional | Number | Must be a number between `0.0` and `1.0` (inclusive) and is used to determine the percentage of requests that fall into the control group (and thus not exposing the ID5 ID). For example, a value of `0.20` will result in 20% of requests without an ID5 ID and 80% with an ID. | `0.1` | -| storage | Required | Object | Storage settings for how the User ID module will cache the ID5 ID locally | | -| storage.type | Required | String | This is where the results of the user ID will be stored. ID5 **requires** `"html5"`. | `"html5"` | -| storage.name | Required | String | The name of the local storage where the user ID will be stored. ID5 **requires** `"id5id"`. | `"id5id"` | -| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. ID5 recommends `90`. | `90` | -| storage.refreshInSeconds | Optional | Integer | How many seconds until the ID5 ID will be refreshed. ID5 strongly recommends 8 hours between refreshes | `8*3600` | +| params.disableExtensions | Optional | Boolean | Set this to `true` to force turn off extensions call. Default `false` | `true` or `false` | +| storage | Required | Object | Storage settings for how the User ID module will cache the ID5 ID locally | | +| storage.type | Required | String | This is where the results of the user ID will be stored. ID5 **requires** `"html5"`. | `"html5"` | +| storage.name | Required | String | The name of the local storage where the user ID will be stored. ID5 **requires** `"id5id"`. | `"id5id"` | +| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. ID5 recommends `90`. | `90` | +| storage.refreshInSeconds | Optional | Integer | How many seconds until the ID5 ID will be refreshed. ID5 strongly recommends 8 hours between refreshes | `8*3600` | **ATTENTION:** As of Prebid.js v4.14.0, ID5 requires `storage.type` to be `"html5"` and `storage.name` to be `"id5id"`. Using other values will display a warning today, but in an upcoming release, it will prevent the ID5 module from loading. This change is to ensure the ID5 module in Prebid.js interoperates properly with the [ID5 API](https://github.com/id5io/id5-api.js) and to reduce the size of publishers' first-party cookies that are sent to their web servers. If you have any questions, please reach out to us at [prebid@id5.io](mailto:prebid@id5.io). diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index a54542f7278..8c0f8ad9cf3 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -5,28 +5,36 @@ import { ID5_PRIVACY_STORAGE_NAME, ID5_STORAGE_NAME, id5IdSubmodule, - nbCacheName, storage, + nbCacheName, + storage, storeInLocalStorage, storeNbInCache, } from 'modules/id5IdSystem.js'; import {coreStorage, init, requestBidsHook, setSubmoduleRegistry} from 'modules/userId/index.js'; import {config} from 'src/config.js'; -import {server} from 'test/mocks/xhr.js'; import * as events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; +import {uspDataHandler} from 'src/adapterManager.js'; import 'src/prebid.js'; import {hook} from '../../../src/hook.js'; import {mockGdprConsent} from '../../helpers/consentData.js'; let expect = require('chai').expect; -describe('ID5 ID System', function() { +describe('ID5 ID System', function () { const ID5_MODULE_NAME = 'id5Id'; const ID5_EIDS_NAME = ID5_MODULE_NAME.toLowerCase(); const ID5_SOURCE = 'id5-sync.com'; const ID5_TEST_PARTNER_ID = 173; const ID5_ENDPOINT = `https://id5-sync.com/g/v2/${ID5_TEST_PARTNER_ID}.json`; + const ID5_API_CONFIG_URL = `https://id5-sync.com/api/config/prebid`; + const ID5_EXTENSIONS_ENDPOINT = 'https://extensions.id5-sync.com/test'; + const ID5_API_CONFIG = { + fetchCall: { + url: ID5_ENDPOINT + } + }; const ID5_NB_STORAGE_NAME = nbCacheName(ID5_TEST_PARTNER_ID); const ID5_STORED_ID = 'storedid5id'; const ID5_STORED_SIGNATURE = '123456'; @@ -58,6 +66,7 @@ describe('ID5 ID System', function() { } } } + function getId5ValueConfig(value) { return { name: ID5_MODULE_NAME, @@ -68,6 +77,7 @@ describe('ID5 ID System', function() { } } } + function getUserSyncConfig(userIds) { return { userSync: { @@ -76,12 +86,15 @@ describe('ID5 ID System', function() { } } } + function getFetchLocalStorageConfig() { return getUserSyncConfig([getId5FetchConfig(ID5_STORAGE_NAME, 'html5')]); } + function getValueConfig(value) { return getUserSyncConfig([getId5ValueConfig(value)]); } + function getAdUnitMock(code = 'adUnit-code') { return { code, @@ -91,15 +104,81 @@ describe('ID5 ID System', function() { }; } + function callSubmoduleGetId(config, consentData, cacheIdObj) { + return new Promise((resolve) => { + id5IdSubmodule.getId(config, consentData, cacheIdObj).callback((response) => { + resolve(response) + }) + }); + } + + class XhrServerMock { + constructor(server) { + this.currentRequestIdx = 0 + this.server = server + } + + expectFirstRequest() { + return this.#expectRequest(0); + } + + expectNextRequest() { + return this.#expectRequest(++this.currentRequestIdx) + } + + expectConfigRequest() { + return this.expectFirstRequest() + .then(configRequest => { + expect(configRequest.url).is.eq(ID5_API_CONFIG_URL); + expect(configRequest.method).is.eq('POST'); + return configRequest; + }) + } + + respondWithConfigAndExpectNext(configRequest, config = ID5_API_CONFIG) { + configRequest.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(config)); + return this.expectNextRequest() + } + + expectFetchRequest() { + return this.expectConfigRequest() + .then(configRequest => { + return this.respondWithConfigAndExpectNext(configRequest, ID5_API_CONFIG); + }).then(request => { + expect(request.url).is.eq(ID5_API_CONFIG.fetchCall.url); + expect(request.method).is.eq('POST'); + return request; + }) + } + + #expectRequest(index) { + let server = this.server + return new Promise(function (resolve) { + (function waitForCondition() { + if (server.requests && server.requests.length > index) return resolve(server.requests[index]); + setTimeout(waitForCondition, 30); + })(); + }) + .then(request => { + return request + }); + } + + hasReceivedAnyRequest() { + let requests = this.server.requests; + return requests && requests.length > 0; + } + } + before(() => { hook.ready(); }); - describe('Check for valid publisher config', function() { - it('should fail with invalid config', function() { + describe('Check for valid publisher config', function () { + it('should fail with invalid config', function () { // no config - expect(id5IdSubmodule.getId()).to.be.eq(undefined); - expect(id5IdSubmodule.getId({ })).to.be.eq(undefined); + expect(id5IdSubmodule.getId()).is.eq(undefined); + expect(id5IdSubmodule.getId({})).is.eq(undefined); // valid params, invalid storage expect(id5IdSubmodule.getId({ params: { partner: 123 } })).to.be.eq(undefined); @@ -113,7 +192,7 @@ describe('ID5 ID System', function() { expect(id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, params: { partner: 'abc' } })).to.be.eq(undefined); }); - it('should warn with non-recommended storage params', function() { + it('should warn with non-recommended storage params', function () { let logWarnStub = sinon.stub(utils, 'logWarn'); id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, params: { partner: 123 } }); @@ -126,189 +205,457 @@ describe('ID5 ID System', function() { }); }); - describe('Xhr Requests from getId()', function() { - const responseHeader = { 'Content-Type': 'application/json' }; - let callbackSpy = sinon.spy(); + describe('Xhr Requests from getId()', function () { + const responseHeader = {'Content-Type': 'application/json'}; - beforeEach(function() { - callbackSpy.resetHistory(); + beforeEach(function () { }); - afterEach(function () { + afterEach(function () { + uspDataHandler.reset() }); it('should call the ID5 server and handle a valid response', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, undefined).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(request.url).to.contain(ID5_ENDPOINT); - expect(request.withCredentials).to.be.true; - expect(requestBody.partner).to.eq(ID5_TEST_PARTNER_ID); - expect(requestBody.o).to.eq('pbjs'); - expect(requestBody.pd).to.be.undefined; - expect(requestBody.s).to.be.undefined; - expect(requestBody.provider).to.be.undefined - expect(requestBody.v).to.eq('$prebid.version$'); - expect(requestBody.gdpr).to.exist; - expect(requestBody.gdpr_consent).to.be.undefined; - expect(requestBody.us_privacy).to.be.undefined; - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.deep.equal(ID5_JSON_RESPONSE); + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let config = getId5FetchConfig(); + let submoduleResponse = callSubmoduleGetId(config, undefined, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(fetchRequest.url).to.contain(ID5_ENDPOINT); + expect(fetchRequest.withCredentials).is.true; + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.pd).is.undefined; + expect(requestBody.s).is.undefined; + expect(requestBody.provider).is.undefined + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.gdpr).is.eq(0); + expect(requestBody.gdpr_consent).is.undefined; + expect(requestBody.us_privacy).is.undefined; + expect(requestBody.storage).is.deep.eq(config.storage) + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(submoduleResponse => { + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); + }); + + it('should call the ID5 server with gdpr data ', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let consentData = { + gdprApplies: true, + consentString: 'consentString' + } + + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.gdpr).to.eq(1); + expect(requestBody.gdpr_consent).is.eq(consentData.consentString); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(submoduleResponse => { + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); + }); + + it('should call the ID5 server without gdpr data when gdpr not applies ', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let consentData = { + gdprApplies: false, + consentString: 'consentString' + } + + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.gdpr).to.eq(0); + expect(requestBody.gdpr_consent).is.undefined + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(submoduleResponse => { + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); + }); + + it('should call the ID5 server with us privacy consent', function () { + let usPrivacyString = '1YN-'; + uspDataHandler.setConsentData(usPrivacyString) + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let consentData = { + gdprApplies: true, + consentString: 'consentString' + } + + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.us_privacy).to.eq(usPrivacyString); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(submoduleResponse => { + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); }); it('should call the ID5 server with no signature field when no stored object', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, undefined).callback; - submoduleCallback(callbackSpy); + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.s).is.undefined; + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.s).to.be.undefined; + it('should call the ID5 server for config with submodule config object', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let id5FetchConfig = getId5FetchConfig(); + id5FetchConfig.params.extraParam = { + x: 'X', + y: { + a: 1, + b: '3' + } + } + let submoduleResponse = callSubmoduleGetId(id5FetchConfig, undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + let requestBody = JSON.parse(configRequest.requestBody) + expect(requestBody).is.deep.eq(id5FetchConfig) + return xhrServerMock.respondWithConfigAndExpectNext(configRequest) + }) + .then(fetchRequest => { + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + it('should call the ID5 server for config with overridden url', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let id5FetchConfig = getId5FetchConfig(); + id5FetchConfig.params.configUrl = 'http://localhost/x/y/z' + + let submoduleResponse = callSubmoduleGetId(id5FetchConfig, undefined, undefined); + + return xhrServerMock.expectFirstRequest() + .then(configRequest => { + expect(configRequest.url).is.eq('http://localhost/x/y/z') + return xhrServerMock.respondWithConfigAndExpectNext(configRequest) + }) + .then(fetchRequest => { + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) }); - it('should call the ID5 server with signature field from stored object', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + it('should call the ID5 server with additional data when provided', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT, + overrides: { + arg1: '123', + arg2: { + x: '1', + y: 2 + } + } + } + }); + }) + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.arg1).is.eq('123') + expect(requestBody.arg2).is.deep.eq({ + x: '1', + y: 2 + }) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.s).to.eq(ID5_STORED_SIGNATURE); + it('should call the ID5 server with extensions', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT + }, + extensionsCall: { + url: ID5_EXTENSIONS_ENDPOINT, + method: 'GET' + } + }); + }) + .then(extensionsRequest => { + expect(extensionsRequest.url).is.eq(ID5_EXTENSIONS_ENDPOINT) + expect(extensionsRequest.method).is.eq('GET') + extensionsRequest.respond(200, responseHeader, JSON.stringify({ + lb: 'ex' + })) + return xhrServerMock.expectNextRequest(); + }) + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.extensions).is.deep.eq({ + lb: 'ex' + }) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); + + it('should call the ID5 server with extensions fetched with POST', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT + }, + extensionsCall: { + url: ID5_EXTENSIONS_ENDPOINT, + method: 'POST', + body: { + x: '1', + y: 2 + } + } + }); + }) + .then(extensionsRequest => { + expect(extensionsRequest.url).is.eq(ID5_EXTENSIONS_ENDPOINT) + expect(extensionsRequest.method).is.eq('POST') + let requestBody = JSON.parse(extensionsRequest.requestBody) + expect(requestBody).is.deep.eq({ + x: '1', + y: 2 + }) + extensionsRequest.respond(200, responseHeader, JSON.stringify({ + lb: 'post', + })) + return xhrServerMock.expectNextRequest(); + }) + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.extensions).is.deep.eq({ + lb: 'post' + }) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + it('should call the ID5 server with signature field from stored object', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.s).is.eq(ID5_STORED_SIGNATURE); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) }); it('should call the ID5 server with pd field when pd config is set', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) const pubData = 'b50ca08271795a8e7e4012813f23d505193d75c0f2e2bb99baa63aa822f66ed3'; let id5Config = getId5FetchConfig(); id5Config.params.pd = pubData; - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.pd).to.eq(pubData); + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.pd).is.eq(pubData); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse; + }) }); it('should call the ID5 server with no pd field when pd config is not set', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5Config = getId5FetchConfig(); id5Config.params.pd = undefined; - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.pd).to.be.undefined; - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.pd).is.undefined; + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse; + }) }); it('should call the ID5 server with nb=1 when no stored value exists and reset after', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.nbPage).to.eq(1); - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.nbPage).is.eq(1); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(() => { + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); + }) }); it('should call the ID5 server with incremented nb when stored value exists and reset after', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) storeNbInCache(ID5_TEST_PARTNER_ID, 1); - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.nbPage).to.eq(2); - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.nbPage).is.eq(2); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(() => { + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); + }) }); it('should call the ID5 server with ab_testing object when abTesting is turned on', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5Config = getId5FetchConfig(); - id5Config.params.abTesting = { enabled: true, controlGroupPct: 0.234 } - - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + id5Config.params.abTesting = {enabled: true, controlGroupPct: 0.234} - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.ab_testing.enabled).to.eq(true); - expect(requestBody.ab_testing.control_group_pct).to.eq(0.234); + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing.enabled).is.eq(true); + expect(requestBody.ab_testing.control_group_pct).is.eq(0.234); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse; + }); }); it('should call the ID5 server without ab_testing object when abTesting is turned off', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5Config = getId5FetchConfig(); - id5Config.params.abTesting = { enabled: false, controlGroupPct: 0.55 } - - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + id5Config.params.abTesting = {enabled: false, controlGroupPct: 0.55} - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.ab_testing).to.be.undefined; + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing).is.undefined; + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }); }); it('should call the ID5 server without ab_testing when when abTesting is not set', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5Config = getId5FetchConfig(); - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.ab_testing).to.be.undefined; + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing).is.undefined; + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }); }); it('should store the privacy object from the ID5 server response', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); - let request = server.requests[0]; - - let responseObject = utils.deepClone(ID5_JSON_RESPONSE); - responseObject.privacy = { + const privacy = { jurisdiction: 'gdpr', id5_consent: true }; - request.respond(200, responseHeader, JSON.stringify(responseObject)); - expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).to.be.eq(JSON.stringify(responseObject.privacy)); - coreStorage.removeDataFromLocalStorage(ID5_PRIVACY_STORAGE_NAME); + + return xhrServerMock.expectFetchRequest() + .then(request => { + let responseObject = utils.deepClone(ID5_JSON_RESPONSE); + responseObject.privacy = privacy; + request.respond(200, responseHeader, JSON.stringify(responseObject)); + return submoduleResponse + }) + .then(() => { + expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).is.eq(JSON.stringify(privacy)); + coreStorage.removeDataFromLocalStorage(ID5_PRIVACY_STORAGE_NAME); + }) }); it('should not store a privacy object if not part of ID5 server response', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) coreStorage.removeDataFromLocalStorage(ID5_PRIVACY_STORAGE_NAME); - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).to.be.null; + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(request => { + let responseObject = utils.deepClone(ID5_JSON_RESPONSE); + responseObject.privacy = undefined; + request.respond(200, responseHeader, JSON.stringify(responseObject)); + return submoduleResponse + }) + .then(() => { + expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).is.null; + }); }); describe('when legacy cookies are set', () => { @@ -327,11 +674,11 @@ describe('ID5 ID System', function() { }) }); - describe('Request Bids Hook', function() { + describe('Request Bids Hook', function () { let adUnits; let sandbox; - beforeEach(function() { + beforeEach(function () { sandbox = sinon.sandbox.create(); mockGdprConsent(sandbox); sinon.stub(events, 'getEvents').returns([]); @@ -340,7 +687,7 @@ describe('ID5 ID System', function() { coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); adUnits = [getAdUnitMock()]; }); - afterEach(function() { + afterEach(function () { events.getEvents.restore(); coreStorage.removeDataFromLocalStorage(ID5_STORAGE_NAME); coreStorage.removeDataFromLocalStorage(`${ID5_STORAGE_NAME}_last`); @@ -359,8 +706,8 @@ describe('ID5 ID System', function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property(`userId.${ID5_EIDS_NAME}`); - expect(bid.userId.id5id.uid).to.equal(ID5_STORED_ID); - expect(bid.userIdAsEids[0]).to.deep.equal({ + expect(bid.userId.id5id.uid).is.equal(ID5_STORED_ID); + expect(bid.userIdAsEids[0]).is.deep.equal({ source: ID5_SOURCE, uids: [{ id: ID5_STORED_ID, @@ -373,7 +720,7 @@ describe('ID5 ID System', function() { }); }); done(); - }, { adUnits }); + }, {adUnits}); }); it('should add config value ID to bids', function (done) { @@ -385,15 +732,15 @@ describe('ID5 ID System', function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property(`userId.${ID5_EIDS_NAME}`); - expect(bid.userId.id5id.uid).to.equal(ID5_STORED_ID); - expect(bid.userIdAsEids[0]).to.deep.equal({ + expect(bid.userId.id5id.uid).is.equal(ID5_STORED_ID); + expect(bid.userIdAsEids[0]).is.deep.equal({ source: ID5_SOURCE, - uids: [{ id: ID5_STORED_ID, atype: 1 }] + uids: [{id: ID5_STORED_ID, atype: 1}] }); }); }); done(); - }, { adUnits }); + }, {adUnits}); }); it('should set nb=1 in cache when no stored nb value exists and cached ID', function (done) { @@ -405,7 +752,7 @@ describe('ID5 ID System', function() { config.setConfig(getFetchLocalStorageConfig()); requestBidsHook((adUnitConfig) => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(1); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(1); done() }, {adUnits}); }); @@ -419,19 +766,20 @@ describe('ID5 ID System', function() { config.setConfig(getFetchLocalStorageConfig()); requestBidsHook(() => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(2); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(2); done() }, {adUnits}); }); it('should call ID5 servers with signature and incremented nb post auction if refresh needed', function () { - storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let initialLocalStorageValue = JSON.stringify(ID5_STORED_OBJ); + storeInLocalStorage(ID5_STORAGE_NAME, initialLocalStorageValue, 1); storeInLocalStorage(`${ID5_STORAGE_NAME}_last`, expDaysStr(-1), 1); - storeNbInCache(ID5_TEST_PARTNER_ID, 1); + storeNbInCache(ID5_TEST_PARTNER_ID, 1); let id5Config = getFetchLocalStorageConfig(); id5Config.userSync.userIds[0].storage.refreshInSeconds = 2; - init(config); setSubmoduleRegistry([id5IdSubmodule]); config.setConfig(id5Config); @@ -441,53 +789,62 @@ describe('ID5 ID System', function() { resolve() }, {adUnits}); }).then(() => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(2); - expect(server.requests).to.be.empty; + expect(xhrServerMock.hasReceivedAnyRequest()).is.false; events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - return new Promise((resolve) => setTimeout(resolve)) - }).then(() => { - let request = server.requests[0]; + return xhrServerMock.expectFetchRequest() + }).then(request => { let requestBody = JSON.parse(request.requestBody); - expect(request.url).to.contain(ID5_ENDPOINT); - expect(requestBody.s).to.eq(ID5_STORED_SIGNATURE); - expect(requestBody.nbPage).to.eq(2); - - const responseHeader = { 'Content-Type': 'application/json' }; + expect(requestBody.s).is.eq(ID5_STORED_SIGNATURE); + expect(requestBody.nbPage).is.eq(2); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(2); + const responseHeader = {'Content-Type': 'application/json'}; request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - expect(decodeURIComponent(getFromLocalStorage(ID5_STORAGE_NAME))).to.be.eq(JSON.stringify(ID5_JSON_RESPONSE)); - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); + return new Promise(function (resolve) { + (function waitForCondition() { + if (getFromLocalStorage(ID5_STORAGE_NAME) !== initialLocalStorageValue) return resolve(); + setTimeout(waitForCondition, 30); + })(); + }) + }).then(() => { + expect(decodeURIComponent(getFromLocalStorage(ID5_STORAGE_NAME))).is.eq(JSON.stringify(ID5_JSON_RESPONSE)); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); }) }); }); - describe('Decode stored object', function() { - const expectedDecodedObject = { id5id: { uid: ID5_STORED_ID, ext: { linkType: ID5_STORED_LINK_TYPE } } }; + describe('Decode stored object', function () { + const expectedDecodedObject = {id5id: {uid: ID5_STORED_ID, ext: {linkType: ID5_STORED_LINK_TYPE}}}; - it('should properly decode from a stored object', function() { - expect(id5IdSubmodule.decode(ID5_STORED_OBJ, getId5FetchConfig())).to.deep.equal(expectedDecodedObject); + it('should properly decode from a stored object', function () { + expect(id5IdSubmodule.decode(ID5_STORED_OBJ, getId5FetchConfig())).is.deep.equal(expectedDecodedObject); }); - it('should return undefined if passed a string', function() { - expect(id5IdSubmodule.decode('somestring', getId5FetchConfig())).to.eq(undefined); + it('should return undefined if passed a string', function () { + expect(id5IdSubmodule.decode('somestring', getId5FetchConfig())).is.eq(undefined); }); }); - describe('A/B Testing', function() { - const expectedDecodedObjectWithIdAbOff = { id5id: { uid: ID5_STORED_ID, ext: { linkType: ID5_STORED_LINK_TYPE } } }; - const expectedDecodedObjectWithIdAbOn = { id5id: { uid: ID5_STORED_ID, ext: { linkType: ID5_STORED_LINK_TYPE, abTestingControlGroup: false } } }; - const expectedDecodedObjectWithoutIdAbOn = { id5id: { uid: '', ext: { linkType: 0, abTestingControlGroup: true } } }; + describe('A/B Testing', function () { + const expectedDecodedObjectWithIdAbOff = {id5id: {uid: ID5_STORED_ID, ext: {linkType: ID5_STORED_LINK_TYPE}}}; + const expectedDecodedObjectWithIdAbOn = { + id5id: { + uid: ID5_STORED_ID, + ext: {linkType: ID5_STORED_LINK_TYPE, abTestingControlGroup: false} + } + }; + const expectedDecodedObjectWithoutIdAbOn = {id5id: {uid: '', ext: {linkType: 0, abTestingControlGroup: true}}}; let testConfig, storedObject; - beforeEach(function() { + beforeEach(function () { testConfig = getId5FetchConfig(); storedObject = utils.deepClone(ID5_STORED_OBJ); }); - describe('A/B Testing Config is Set', function() { + describe('A/B Testing Config is Set', function () { let randStub; - beforeEach(function() { - randStub = sinon.stub(Math, 'random').callsFake(function() { + beforeEach(function () { + randStub = sinon.stub(Math, 'random').callsFake(function () { return 0.25; }); }); @@ -495,39 +852,39 @@ describe('ID5 ID System', function() { randStub.restore(); }); - describe('Decode', function() { + describe('Decode', function () { let logErrorSpy; - beforeEach(function() { + beforeEach(function () { logErrorSpy = sinon.spy(utils, 'logError'); }); - afterEach(function() { + afterEach(function () { logErrorSpy.restore(); }); it('should not set abTestingControlGroup extension when A/B testing is off', function () { let decoded = id5IdSubmodule.decode(storedObject, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOff); }); it('should set abTestingControlGroup to false when A/B testing is on but in normal group', function () { - storedObject.ab_testing = { result: 'normal' }; + storedObject.ab_testing = {result: 'normal'}; let decoded = id5IdSubmodule.decode(storedObject, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOn); + expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOn); }); it('should not expose ID when everyone is in control group', function () { - storedObject.ab_testing = { result: 'control' }; + storedObject.ab_testing = {result: 'control'}; storedObject.universal_uid = ''; storedObject.link_type = 0; let decoded = id5IdSubmodule.decode(storedObject, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithoutIdAbOn); + expect(decoded).is.deep.equal(expectedDecodedObjectWithoutIdAbOn); }); it('should log A/B testing errors', function () { - storedObject.ab_testing = { result: 'error' }; + storedObject.ab_testing = {result: 'error'}; let decoded = id5IdSubmodule.decode(storedObject, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOff); sinon.assert.calledOnce(logErrorSpy); }); }); From 4a3daec6f1854f1b323140b84e8edb8e93d4b699 Mon Sep 17 00:00:00 2001 From: JacobKlein26 <42449375+JacobKlein26@users.noreply.github.com> Date: Thu, 18 Aug 2022 22:28:54 -0400 Subject: [PATCH 166/569] NextMillenium Bid Adapter: Remove ortb2 referrerInfo (#8868) * remove ortb2, get device/site manually * updated tests * remove fallbacks * no need to craete variable if there is no fallback (return in place) * removed one test case Co-authored-by: Yakov Klein --- modules/nextMillenniumBidAdapter.js | 31 +++++++++++++++---- .../modules/nextMillenniumBidAdapter_spec.js | 9 ++---- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index f873e5b5c29..87d2e53568f 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -26,11 +26,12 @@ export const spec = { _each(validBidRequests, function(bid) { window.nmmRefreshCounts[bid.adUnitCode] = window.nmmRefreshCounts[bid.adUnitCode] || 0; const id = getPlacementId(bid) + let sizes = bid.sizes + if (sizes && !Array.isArray(sizes[0])) sizes = [sizes] + + const site = getSiteObj() + const device = getDeviceObj() - if (bid.sizes && !Array.isArray(bid.sizes[0])) bid.sizes = [bid.sizes] - if (!bid.ortb2) bid.ortb2 = {} - if (!bid.ortb2.device) bid.ortb2.device = {} - bid.ortb2.device.referrer = (getRefererInfo && getRefererInfo().ref) || '' const postBody = { 'id': bid.auctionId, 'ext': { @@ -46,10 +47,11 @@ export const spec = { 'scrollTop': window.pageYOffset || document.documentElement.scrollTop } }, - ...bid.ortb2, + device, + site, 'imp': [{ 'banner': { - 'format': (bid.sizes || []).map(s => { return {w: s[0], h: s[1]} }) + 'format': (sizes || []).map(s => { return {w: s[0], h: s[1]} }) }, 'ext': { 'prebid': { @@ -198,4 +200,21 @@ function getTopWindow(curWindow, nesting = 0) { } } +function getSiteObj() { + const refInfo = (getRefererInfo && getRefererInfo()) || {} + + return { + page: refInfo.page, + ref: refInfo.ref, + domain: refInfo.domain + } +} + +function getDeviceObj() { + return { + w: window.innerWidth || window.document.documentElement.clientWidth || window.document.body.clientWidth || 0, + h: window.innerHeight || window.document.documentElement.clientHeight || window.document.body.clientHeight || 0, + } +} + registerBidder(spec); diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 40005356fd8..303025888dc 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -104,9 +104,9 @@ describe('nextMillenniumBidAdapterTests', function() { expect(JSON.parse(request[0].data).ext.nextMillennium.refresh_count).to.equal(3); }); - it('Check if ORTB was added', function() { + it('Check if domain was added', function() { const request = spec.buildRequests(bidRequestData) - expect(JSON.parse(request[0].data).site.domain).to.equal('example.com') + expect(JSON.parse(request[0].data).site.domain).to.exist }) it('Check if elOffsets was added', function() { @@ -114,11 +114,6 @@ describe('nextMillenniumBidAdapterTests', function() { expect(JSON.parse(request[0].data).ext.nextMillennium.elOffsets).to.be.an('object') }) - it('Check if refferer was added', function() { - const request = spec.buildRequests(bidRequestData) - expect(JSON.parse(request[0].data).device.referrer).to.exist - }) - it('Check if imp object was added', function() { const request = spec.buildRequests(bidRequestData) expect(JSON.parse(request[0].data).imp).to.be.an('array') From 6f0a824d40d706d044b9b4c0070aed0c04bd5942 Mon Sep 17 00:00:00 2001 From: caseywhitmire <60086994+caseywhitmire@users.noreply.github.com> Date: Fri, 19 Aug 2022 08:26:29 -0700 Subject: [PATCH 167/569] adserver.js : remove unused code (#8855) --- src/adserver.js | 56 ------------------------------------------------- 1 file changed, 56 deletions(-) diff --git a/src/adserver.js b/src/adserver.js index 8d99b29c3ef..db7aaaa1dc8 100644 --- a/src/adserver.js +++ b/src/adserver.js @@ -1,61 +1,5 @@ -import { formatQS } from './utils.js'; -import { targeting } from './targeting.js'; import {hook} from './hook.js'; -// Adserver parent class -const AdServer = function(attr) { - this.name = attr.adserver; - this.code = attr.code; - this.getWinningBidByCode = function() { - return targeting.getWinningBids(this.code)[0]; - }; -}; - -// DFP ad server -// TODO: this seems to be unused? -export function dfpAdserver(options, urlComponents) { - var adserver = new AdServer(options); - adserver.urlComponents = urlComponents; - - var dfpReqParams = { - 'env': 'vp', - 'gdfp_req': '1', - 'impl': 's', - 'unviewed_position_start': '1' - }; - - var dfpParamsWithVariableValue = ['output', 'iu', 'sz', 'url', 'correlator', 'description_url', 'hl']; - - var getCustomParams = function(targeting) { - return encodeURIComponent(formatQS(targeting)); - }; - - adserver.appendQueryParams = function() { - var bid = adserver.getWinningBidByCode(); - if (bid) { - this.urlComponents.search.description_url = encodeURIComponent(bid.vastUrl); - this.urlComponents.search.cust_params = getCustomParams(bid.adserverTargeting); - this.urlComponents.search.correlator = Date.now(); - } - }; - - adserver.verifyAdserverTag = function() { - for (var key in dfpReqParams) { - if (!this.urlComponents.search.hasOwnProperty(key) || this.urlComponents.search[key] !== dfpReqParams[key]) { - return false; - } - } - for (var i in dfpParamsWithVariableValue) { - if (!this.urlComponents.search.hasOwnProperty(dfpParamsWithVariableValue[i])) { - return false; - } - } - return true; - }; - - return adserver; -}; - /** * return the GAM PPID, if available (eid for the userID configured with `userSync.ppidSource`) */ From 3a029f684ee3992233e4de1cfa5b1cfbe19d6667 Mon Sep 17 00:00:00 2001 From: philan15 <37775368+philan15@users.noreply.github.com> Date: Fri, 19 Aug 2022 19:21:46 +0300 Subject: [PATCH 168/569] Displayio Bid Adapter: custom render; fix eids payload (#8847) * Custom render; call pubmatic get user id function is removed * use refererInfo; remove call createEidsArray --- modules/displayioBidAdapter.js | 189 +++++++++--------- test/spec/modules/displayioBidAdapter_spec.js | 68 ++----- 2 files changed, 108 insertions(+), 149 deletions(-) diff --git a/modules/displayioBidAdapter.js b/modules/displayioBidAdapter.js index e039d461fc7..c3c6597dd1b 100644 --- a/modules/displayioBidAdapter.js +++ b/modules/displayioBidAdapter.js @@ -1,16 +1,16 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {Renderer} from '../src/Renderer.js'; +import {getWindowFromDocument, logWarn} from '../src/utils.js'; -const BIDDER_VERSION = '1.0.0'; +const ADAPTER_VERSION = '1.1.0'; const BIDDER_CODE = 'displayio'; -const GVLID = 999; const BID_TTL = 300; const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const DEFAULT_CURRENCY = 'USD'; export const spec = { code: BIDDER_CODE, - gvlid: GVLID, supportedMediaTypes: SUPPORTED_AD_TYPES, isBidRequestValid: function(bid) { return !!(bid.params && bid.params.placementId && bid.params.siteId && @@ -20,7 +20,7 @@ export const spec = { return bidRequests.map(bid => { let url = '//' + bid.params.adsSrvDomain + '/srv?method=getPlacement&app=' + bid.params.siteId + '&placement=' + bid.params.placementId; - const data = this._getPayload(bid, bidderRequest); + const data = getPayload(bid, bidderRequest); return { method: 'POST', headers: {'Content-Type': 'application/json;charset=utf-8'}, @@ -42,117 +42,112 @@ export const spec = { height: adData.h, netRevenue: true, ttl: BID_TTL, - creativeId: adData.adId || 0, - currency: DEFAULT_CURRENCY, + creativeId: adData.adId || 1, + currency: adData.cur || DEFAULT_CURRENCY, referrer: data.data.ref, - mediaType: ads[0].ad.subtype, + mediaType: ads[0].ad.subtype === 'videoVast' ? VIDEO : BANNER, ad: adData.markup, - placement: data.placement, + adUnitCode: data.adUnitCode, + renderURL: data.renderURL, adData: adData }; - if (bidResponse.vastUrl === 'videoVast') { - bidResponse.vastUrl = adData.videos[0].url + + if (bidResponse.mediaType === VIDEO) { + bidResponse.vastUrl = adData.videos[0] && adData.videos[0].url + } + + if (bidResponse.renderURL) { + bidResponse.renderer = newRenderer(bidResponse); } bidResponses.push(bidResponse); } return bidResponses; - }, - _getPayload: function (bid, bidderRequest) { - const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; - const userSession = 'us_web_xxxxxxxxxxxx'.replace(/[x]/g, c => { - let r = Math.random() * 16 | 0; - let v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - const { params } = bid; - const { siteId, placementId } = params; - const { refererInfo, uspConsent, gdprConsent } = bidderRequest; - const mediation = {consent: '-1', gdpr: '-1'}; - if (gdprConsent) { - if (gdprConsent.consentString !== undefined) { - mediation.consent = gdprConsent.consentString; - } - if (gdprConsent.gdprApplies !== undefined) { - mediation.gdpr = gdprConsent.gdprApplies ? '1' : '0'; - } + } +}; + +function getPayload (bid, bidderRequest) { + const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; + const userSession = 'us_web_xxxxxxxxxxxx'.replace(/[x]/g, c => { + let r = Math.random() * 16 | 0; + let v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + const { params, adUnitCode, bidId } = bid; + const { siteId, placementId, renderURL, pageCategory, keywords } = params; + const { refererInfo, uspConsent, gdprConsent } = bidderRequest; + const mediation = {consent: '-1', gdpr: '-1'}; + if (gdprConsent && 'gdprApplies' in gdprConsent) { + if (gdprConsent.consentString !== undefined) { + mediation.consent = gdprConsent.consentString; } - const payload = { - userSession, + if (gdprConsent.gdprApplies !== undefined) { + mediation.gdpr = gdprConsent.gdprApplies ? '1' : '0'; + } + } + return { + userSession, + data: { + id: bidId, + action: 'getPlacement', + app: siteId, + placement: placementId, + adUnitCode, + renderURL, data: { - id: bid.bidId, - action: 'getPlacement', - app: siteId, - placement: placementId, - data: { - pagecat: params.pageCategory ? params.pageCategory.split(',').map(k => k.trim()) : [], - keywords: params.keywords ? params.keywords.split(',').map(k => k.trim()) : [], - lang_content: document.documentElement.lang, - lang: window.navigator.language, - // TODO: are these the correct refererInfo values? - domain: refererInfo.domain, - page: refererInfo.page, - ref: refererInfo.ref, - userids: _getUserIDs(), - geo: '', - }, - complianceData: { - child: '-1', - us_privacy: uspConsent, - dnt: window.navigator.doNotTrack, - iabConsent: {}, - mediation: { - consent: mediation.consent, - gdpr: mediation.gdpr, - } - }, - integration: 'JS', - omidpn: 'Displayio', - mediationPlatform: 0, - prebidVersion: BIDDER_VERSION, - device: { - w: window.screen.width, - h: window.screen.height, - connection_type: connection ? connection.effectiveType : '', + pagecat: pageCategory ? pageCategory.split(',').map(k => k.trim()) : [], + keywords: keywords ? keywords.split(',').map(k => k.trim()) : [], + lang_content: document.documentElement.lang, + lang: window.navigator.language, + domain: refererInfo.domain, + page: refererInfo.page, + ref: refererInfo.referer, + userids: bid.userIdAsEids || {}, + geo: '', + }, + complianceData: { + child: '-1', + us_privacy: uspConsent, + dnt: window.doNotTrack === '1' || window.navigator.doNotTrack === '1' || false, + iabConsent: {}, + mediation: { + consent: mediation.consent, + gdpr: mediation.gdpr, } + }, + integration: 'JS', + omidpn: 'Displayio', + mediationPlatform: 0, + prebidVersion: ADAPTER_VERSION, + device: { + w: window.screen.width, + h: window.screen.height, + connection_type: connection ? connection.effectiveType : '', } } - if (navigator.permissions) { - navigator.permissions.query({ name: 'geolocation' }) - .then((result) => { - if (result.state === 'granted') { - payload.data.data.geo = _getGeoData(); - } - }); - } - return payload } -}; +} + +function newRenderer(bid) { + const renderer = Renderer.install({ + id: bid.requestId, + url: bid.renderURL, + adUnitCode: bid.adUnitCode + }); -function _getUserIDs () { - let ids = {}; try { - ids = window.owpbjs.getUserIdsAsEids(); - } catch (e) {} - return ids; + renderer.setRender(webisRender); + } catch (err) { + logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; } -async function _getGeoData () { - let geoData = null; - const getCurrentPosition = () => { - return new Promise((resolve, reject) => - navigator.geolocation.getCurrentPosition(resolve, reject) - ); - } - try { - const position = await getCurrentPosition(); - let {latitude, longitude, accuracy} = position.coords; - geoData = { - 'lat': latitude, - 'lng': longitude, - 'precision': accuracy - }; - } catch (e) {} - return geoData +function webisRender(bid, doc) { + bid.renderer.push(() => { + const win = getWindowFromDocument(doc) || window; + win.webis.init(bid.adData, bid.adUnitCode, bid.params); + }) } registerBidder(spec); diff --git a/test/spec/modules/displayioBidAdapter_spec.js b/test/spec/modules/displayioBidAdapter_spec.js index dfeb67fb467..56b8b85384b 100644 --- a/test/spec/modules/displayioBidAdapter_spec.js +++ b/test/spec/modules/displayioBidAdapter_spec.js @@ -1,5 +1,5 @@ -import { expect } from 'chai' import {spec} from 'modules/displayioBidAdapter.js' +import {BANNER} from '/src/mediaTypes' describe('Displayio adapter', function () { const BIDDER = 'displayio' @@ -12,10 +12,7 @@ describe('Displayio adapter', function () { mediaTypes: { banner: { sizes: [[320, 480]] - }, - video: { - sizes: [[360, 640]] - }, + } }, params: { siteId: 1, @@ -128,53 +125,6 @@ describe('Displayio adapter', function () { }) }) - describe('_getPayload', function () { - const payload = spec._getPayload(bidRequests[0], bidderRequest) - it('should not be empty', function() { - expect(payload).to.not.be.empty - }) - - it('should have userSession', function() { - expect(payload.userSession).to.be.a('string') - }) - - it('should have data object', function() { - expect(payload.data).to.be.a('object') - }) - - it('should have complianceData object', function() { - expect(payload.data.complianceData).to.be.a('object') - }) - - it('should have device object', function() { - expect(payload.data.device).to.be.a('object') - }) - - it('should have omidpn', function() { - expect(payload.data.omidpn).to.be.a('string') - }) - - it('should have integration', function() { - expect(payload.data.integration).to.be.a('string') - }) - - it('should have bidId', function() { - expect(payload.data.id).to.not.be.empty - }) - - it('should have action getPlacement', function() { - expect(payload.data.action).to.be.equal('getPlacement') - }) - - it('should have app parameter', function() { - expect(payload.data.app).to.be.a('number') - }) - - it('should have placement parameter', function() { - expect(payload.data.placement).to.be.a('number') - }) - }) - describe('interpretResponse', function () { const response = { body: { @@ -199,6 +149,8 @@ describe('Displayio adapter', function () { data: { data: { id: 'id_001', + adUnitCode: 'test-div', + renderURL: 'testprebid.com/render.js', data: { ref: 'testprebid.com' } @@ -235,5 +187,17 @@ describe('Displayio adapter', function () { it('should have ad', function() { expect(ir.ad).to.be.a('string') }) + + it('should have mediaType', function() { + expect(ir.mediaType).to.be.equal(BANNER) + }) + + it('should have adUnitCode', function() { + expect(ir.adUnitCode).to.be.a('string') + }) + + it('should have renderURL', function() { + expect(ir.renderURL).to.be.a('string') + }) }) }) From 7e5548e4de15edb3b554fcd6c8a19a26c0bd36e2 Mon Sep 17 00:00:00 2001 From: Gena Date: Fri, 19 Aug 2022 21:46:50 +0200 Subject: [PATCH 169/569] VidCrunch LLC bidder (#8872) --- modules/adtelligentBidAdapter.js | 2 ++ test/spec/modules/adtelligentBidAdapter_spec.js | 1 + 2 files changed, 3 insertions(+) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 2ee5b0f72a3..3dad2e98bcf 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -23,6 +23,7 @@ const HOST_GETTERS = { janet: () => 'ghb.bidder.jmgads.com', pgam: () => 'ghb.pgamssp.com', ocm: () => 'ghb.cenarius.orangeclickmedia.com', + vidcrunchllc: () => 'ghb.platform.vidcrunch.com', } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -43,6 +44,7 @@ export const spec = { { code: 'navelix', gvlid: 380 }, 'pgam', 'ocm', + { code: 'vidcrunchllc', gvlid: 1145 }, ], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index a9b9724da3a..d0ef69ccf08 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -20,6 +20,7 @@ const aliasEP = { janet: 'https://ghb.bidder.jmgads.com/v2/auction/', pgam: 'https://ghb.pgamssp.com/v2/auction/', ocm: 'https://ghb.cenarius.orangeclickmedia.com/v2/auction/', + vidcrunchllc: 'https://ghb.platform.vidcrunch.com/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; From 0b71a3364cf774a87f630157ab78c1a4f0670c18 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Mon, 22 Aug 2022 17:51:05 +0200 Subject: [PATCH 170/569] bidWatch Analytics Adapter : limit bandwidth usage + refactory (#8774) * bidWatch Analytics Adapter : code refactory * Update bidwatchAnalyticsAdapter_spec.js * Update bidwatchAnalyticsAdapter_spec.js --- modules/bidwatchAnalyticsAdapter.js | 171 ++++++++++++------ .../modules/bidwatchAnalyticsAdapter_spec.js | 40 ++-- 2 files changed, 140 insertions(+), 71 deletions(-) diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index ef63fee53d5..9983eb58289 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -12,6 +12,7 @@ const { BID_WON, BID_RESPONSE, BID_REQUESTED, + BID_TIMEOUT, } } = CONSTANTS; @@ -20,80 +21,125 @@ let allEvents = {} let auctionEnd = {} let initOptions = {} let endpoint = 'https://default' -let objectToSearchForBidderCode = ['bidderRequests', 'bidsReceived', 'noBids'] +let requestsAttributes = ['adUnitCode', 'auctionId', 'bidder', 'bidderCode', 'bidId', 'cpm', 'creativeId', 'currency', 'width', 'height', 'mediaType', 'netRevenue', 'originalCpm', 'originalCurrency', 'requestId', 'size', 'source', 'status', 'timeToRespond', 'transactionId', 'ttl', 'sizes', 'mediaTypes', 'src', 'params', 'userId', 'labelAny', 'bids']; function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; } -function cleanArgObject(arg, removead) { - if (typeof arg['bidderCode'] == 'string') { arg['originalBidder'] = getAdapterNameForAlias(arg['bidderCode']); } - if (typeof arg['creativeId'] == 'number') { - arg['creativeId'] = arg['creativeId'].toString(); - } - if (removead && typeof arg['ad'] != 'undefined') { - arg['ad'] = 'emptied'; - } - if (typeof arg['gdprConsent'] != 'undefined' && typeof arg['gdprConsent']['vendorData'] != 'undefined') { - arg['gdprConsent']['vendorData'] = 'emptied'; +function filterAttributes(arg, removead) { + let response = {}; + if (typeof arg == 'object') { + if (typeof arg['bidderCode'] == 'string') { + response['originalBidder'] = getAdapterNameForAlias(arg['bidderCode']); + } else if (typeof arg['bidder'] == 'string') { + response['originalBidder'] = getAdapterNameForAlias(arg['bidder']); + } + if (!removead && typeof arg['ad'] != 'undefined') { + response['ad'] = arg['ad']; + } + if (typeof arg['gdprConsent'] != 'undefined') { + response['gdprConsent'] = {}; + if (typeof arg['gdprConsent']['consentString'] != 'undefined') { response['gdprConsent']['consentString'] = arg['gdprConsent']['consentString']; } + } + requestsAttributes.forEach((attr) => { + if (typeof arg[attr] != 'undefined') { response[attr] = arg[attr]; } + }); + if (typeof response['creativeId'] == 'number') { response['creativeId'] = response['creativeId'].toString(); } } - return arg; + return response; } -function cleanArgs(arg, removead) { - Object.keys(arg).forEach(key => { - arg[key] = cleanArgObject(arg[key], removead); +function cleanAuctionEnd(args) { + let response = {}; + let filteredObj; + let objects = ['bidderRequests', 'bidsReceived', 'noBids']; + objects.forEach((attr) => { + if (Array.isArray(args[attr])) { + response[attr] = []; + args[attr].forEach((obj) => { + filteredObj = filterAttributes(obj, true); + if (typeof obj['bids'] == 'object') { + filteredObj['bids'] = []; + obj['bids'].forEach((bid) => { + filteredObj['bids'].push(filterAttributes(bid, true)); + }); + } + response[attr].push(filteredObj); + }); + } }); - return arg + return response; +} + +function cleanCreatives(args) { + return filterAttributes(args, false); } -function checkBidderCode(args, removead) { - if (typeof args == 'object') { - for (let i = 0; i < objectToSearchForBidderCode.length; i++) { - if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = cleanArgs(args[objectToSearchForBidderCode[i]], removead) } +function enhanceMediaType(arg) { + saveEvents['bidRequested'].forEach((bidRequested) => { + if (bidRequested['auctionId'] == arg['auctionId'] && Array.isArray(bidRequested['bids'])) { + bidRequested['bids'].forEach((bid) => { + if (bid['transactionId'] == arg['transactionId'] && bid['bidId'] == arg['requestId']) { arg['mediaTypes'] = bid['mediaTypes']; } + }); } - } - if (typeof args['bidderCode'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidderCode']); } else if (typeof args['bidder'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidder']); } - if (typeof args['creativeId'] == 'number') { args['creativeId'] = args['creativeId'].toString(); } + }); + return arg; +} - return args +function addBidResponse(args) { + let eventType = BID_RESPONSE; + let argsCleaned = cleanCreatives(JSON.parse(JSON.stringify(args))); ; + if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } + allEvents[eventType].push(argsCleaned); } -function addEvent(eventType, args) { - let argsCleaned; - if (eventType && args) { - if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } - if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } - argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), false); - allEvents[eventType].push(argsCleaned); - saveEvents[eventType].push(argsCleaned); - argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), true); - if (['auctionend', 'bidtimeout'].includes(eventType.toLowerCase())) { - if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } - auctionEnd[eventType].push(argsCleaned); - } - } +function addBidRequested(args) { + let eventType = BID_REQUESTED; + let argsCleaned = filterAttributes(args, true); + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } + saveEvents[eventType].push(argsCleaned); +} + +function addTimeout(args) { + let eventType = BID_TIMEOUT; + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } + saveEvents[eventType].push(args); + let argsCleaned = []; + let argsDereferenced = JSON.parse(JSON.stringify(args)); + argsDereferenced.forEach((attr) => { + argsCleaned.push(filterAttributes(JSON.parse(JSON.stringify(attr)), false)); + }); + if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } + auctionEnd[eventType].push(argsCleaned); +} + +function addAuctionEnd(args) { + let eventType = AUCTION_END; + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } + saveEvents[eventType].push(args); + let argsCleaned = cleanAuctionEnd(JSON.parse(JSON.stringify(args))); + if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } + auctionEnd[eventType].push(argsCleaned); } function handleBidWon(args) { - args = cleanArgObject(JSON.parse(JSON.stringify(args)), true); + args = enhanceMediaType(filterAttributes(JSON.parse(JSON.stringify(args)), true)); let increment = args['cpm']; if (typeof saveEvents['auctionEnd'] == 'object') { - for (let i = 0; i < saveEvents['auctionEnd'].length; i++) { - let tmpAuction = saveEvents['auctionEnd'][i]; - if (tmpAuction['auctionId'] == args['auctionId'] && typeof tmpAuction['bidsReceived'] == 'object') { - for (let j = 0; j < tmpAuction['bidsReceived'].length; j++) { - let tmpBid = tmpAuction['bidsReceived'][j]; - if (tmpBid['transactionId'] == args['transactionId'] && tmpBid['adId'] != args['adId']) { - if (args['cpm'] < tmpBid['cpm']) { + saveEvents['auctionEnd'].forEach((auction) => { + if (auction['auctionId'] == args['auctionId'] && typeof auction['bidsReceived'] == 'object') { + auction['bidsReceived'].forEach((bid) => { + if (bid['transactionId'] == args['transactionId'] && bid['adId'] != args['adId']) { + if (args['cpm'] < bid['cpm']) { increment = 0; - } else if (increment > args['cpm'] - tmpBid['cpm']) { - increment = args['cpm'] - tmpBid['cpm']; + } else if (increment > args['cpm'] - bid['cpm']) { + increment = args['cpm'] - bid['cpm']; } } - } + }); } - } + }); } args['cpmIncrement'] = increment; if (typeof saveEvents.bidRequested == 'object' && saveEvents.bidRequested.length > 0 && saveEvents.bidRequested[0].gdprConsent) { args.gdpr = saveEvents.bidRequested[0].gdprConsent; } @@ -101,12 +147,16 @@ function handleBidWon(args) { } function handleAuctionEnd() { - ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); - auctionEnd = {} - if (typeof allEvents['bidResponse'] != 'undefined') { - for (let i = 0; i < allEvents['bidResponse'].length; i++) { ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(allEvents['bidResponse'][i]), {method: 'POST', withCredentials: true}); } - } - allEvents = {} + ajax(endpoint + '.bidwatch.io/analytics/auctions', function (data) { + let list = JSON.parse(data); + if (Array.isArray(list) && typeof allEvents['bidResponse'] != 'undefined') { + allEvents['bidResponse'].forEach((bidResponse) => { + if (list.includes(bidResponse['originalBidder'] + '_' + bidResponse['creativeId'])) { ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(bidResponse), {method: 'POST', withCredentials: true}); } + }); + } + allEvents = {}; + }, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); + auctionEnd = {}; } let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { @@ -116,17 +166,20 @@ let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { }) { switch (eventType) { case AUCTION_END: - addEvent(eventType, args); + addAuctionEnd(args); handleAuctionEnd(); break; case BID_WON: handleBidWon(args); break; case BID_RESPONSE: - addEvent(eventType, args); + addBidResponse(args); break; case BID_REQUESTED: - addEvent(eventType, args); + addBidRequested(args); + break; + case BID_TIMEOUT: + addTimeout(args); break; } }}); diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js index f827f068bb3..dddb29dbfd9 100644 --- a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js @@ -260,15 +260,15 @@ describe('BidWatch Analytics', function () { describe('main test flow', function () { beforeEach(function () { sinon.stub(events, 'getEvents').returns([]); + sinon.spy(bidwatchAnalytics, 'track'); }); - afterEach(function () { events.getEvents.restore(); + bidwatchAnalytics.disableAnalytics(); + bidwatchAnalytics.track.restore(); }); - it('should catch events of interest', function () { - sinon.spy(bidwatchAnalytics, 'track'); - + it('test auctionEnd', function () { adapterManager.registerAnalyticsAdapter({ code: 'bidwatch', adapter: bidwatchAnalytics @@ -280,6 +280,9 @@ describe('BidWatch Analytics', function () { domain: 'test' } }); + + events.emit(constants.EVENTS.BID_REQUESTED, auctionEnd['bidderRequests'][0]); + events.emit(constants.EVENTS.BID_RESPONSE, auctionEnd['bidsReceived'][0]); events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); events.emit(constants.EVENTS.AUCTION_END, auctionEnd); expect(server.requests.length).to.equal(1); @@ -287,18 +290,31 @@ describe('BidWatch Analytics', function () { expect(message).to.have.property('auctionEnd').exist; expect(message.auctionEnd).to.have.lengthOf(1); expect(message.auctionEnd[0]).to.have.property('bidsReceived').and.to.have.lengthOf(1); - expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('ad'); - expect(message.auctionEnd[0].bidsReceived[0].ad).to.equal('emptied'); + expect(message.auctionEnd[0].bidsReceived[0]).not.to.have.property('ad'); expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); - expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).to.have.property('vendorData'); - expect(message.auctionEnd[0].bidderRequests[0].gdprConsent.vendorData).to.equal('emptied'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).not.to.have.property('vendorData'); + sinon.assert.callCount(bidwatchAnalytics.track, 4); + }); + + it('test bidWon', function() { + adapterManager.registerAnalyticsAdapter({ + code: 'bidwatch', + adapter: bidwatchAnalytics + }); + + adapterManager.enableAnalytics({ + provider: 'bidwatch', + options: { + domain: 'test' + } + }); events.emit(constants.EVENTS.BID_WON, bidWon); - expect(server.requests.length).to.equal(2); - message = JSON.parse(server.requests[1].requestBody); - expect(message).to.have.property('ad').and.to.equal('emptied'); + expect(server.requests.length).to.equal(1); + let message = JSON.parse(server.requests[0].requestBody); + expect(message).not.to.have.property('ad'); expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); - sinon.assert.callCount(bidwatchAnalytics.track, 3); + sinon.assert.callCount(bidwatchAnalytics.track, 1); }); }); }); From a61f4bfdc31c6ade12c19489b6edbe2126711277 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 13:37:11 -0400 Subject: [PATCH 171/569] Bump tibdex/github-app-token from 1.3.0 to 1.6 (#8878) Bumps [tibdex/github-app-token](https://github.com/tibdex/github-app-token) from 1.3.0 to 1.6. - [Release notes](https://github.com/tibdex/github-app-token/releases) - [Commits](https://github.com/tibdex/github-app-token/compare/36464acb844fc53b9b8b2401da68844f6b05ebb0...f717b5ecd4534d3c4df4ce9b5c1c2214f0f7cd06) --- updated-dependencies: - dependency-name: tibdex/github-app-token dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/issue_tracker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue_tracker.yml b/.github/workflows/issue_tracker.yml index fa33ffe5c53..05d08b2b0d7 100644 --- a/.github/workflows/issue_tracker.yml +++ b/.github/workflows/issue_tracker.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Generate token id: generate_token - uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0 + uses: tibdex/github-app-token@f717b5ecd4534d3c4df4ce9b5c1c2214f0f7cd06 with: app_id: ${{ secrets.ISSUE_APP_ID }} private_key: ${{ secrets.ISSUE_APP_PEM }} From 5be582f2e35650e0bf4dbe212bde9e936edfe75c Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Mon, 22 Aug 2022 20:44:12 +0300 Subject: [PATCH 172/569] Adkernel Bid Adapter: add impression-level FPD support (#8880) --- modules/adkernelBidAdapter.js | 40 ++++++++++++++------ test/spec/modules/adkernelBidAdapter_spec.js | 13 ++++++- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 78f00784462..ff7d3be6ebf 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -14,11 +14,12 @@ import { isPlainObject, isStr, mergeDeep, - parseGPTSingleSizeArrayToRtbSize + parseGPTSingleSizeArrayToRtbSize, + getDefinedParams } from '../src/utils.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {find, includes} from '../src/polyfill.js'; +import {find} from '../src/polyfill.js'; import {config} from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; @@ -28,10 +29,11 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; * * Please contact prebid@adkernel.com and we'll add your adapter as an alias. */ - -const VIDEO_TARGETING = Object.freeze(['mimes', 'minduration', 'maxduration', 'protocols', - 'startdelay', 'linearity', 'boxingallowed', 'playbackmethod', 'delivery', - 'pos', 'api', 'ext']); +const VIDEO_PARAMS = ['pos', 'context', 'placement', 'api', 'mimes', 'protocols', 'playbackmethod', 'minduration', 'maxduration', + 'startdelay', 'linearity', 'skip', 'skipmin', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackend', 'boxingallowed']; +const VIDEO_FPD = ['battr', 'pos']; +const NATIVE_FPD = ['battr', 'api']; +const BANNER_FPD = ['btype', 'battr', 'pos', 'api']; const VERSION = '1.6'; const SYNC_IFRAME = 1; const SYNC_IMAGE = 2; @@ -275,18 +277,18 @@ function buildImp(bidRequest, secure) { format: sizes.map(wh => parseGPTSingleSizeArrayToRtbSize(wh)), topframe: 0 }; + populateImpFpd(imp.banner, bidRequest, BANNER_FPD); mediaType = BANNER; } else if (deepAccess(bidRequest, 'mediaTypes.video')) { let video = deepAccess(bidRequest, 'mediaTypes.video'); - imp.video = {}; + imp.video = getDefinedParams(video, VIDEO_PARAMS); + populateImpFpd(imp.video, bidRequest, VIDEO_FPD); if (video.playerSize) { sizes = video.playerSize[0]; imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {}); - } - if (bidRequest.params.video) { - Object.keys(bidRequest.params.video) - .filter(key => includes(VIDEO_TARGETING, key)) - .forEach(key => imp.video[key] = bidRequest.params.video[key]); + } else if (video.w && video.h) { + imp.video.w = video.w; + imp.video.h = video.h; } mediaType = VIDEO; } else if (deepAccess(bidRequest, 'mediaTypes.native')) { @@ -295,6 +297,7 @@ function buildImp(bidRequest, secure) { ver: '1.1', request: JSON.stringify(nativeRequest) }; + populateImpFpd(imp.native, bidRequest, NATIVE_FPD); mediaType = NATIVE; } else { throw new Error('Unsupported bid received'); @@ -338,6 +341,19 @@ function buildNativeRequest(nativeReq) { return request; } +/** + * Populate impression-level FPD from bid request + * @param target {Object} + * @param bidRequest {BidRequest} + * @param props {String[]} + */ +function populateImpFpd(target, bidRequest, props) { + if (bidRequest.ortb2Imp === undefined) { + return; + } + Object.assign(target, getDefinedParams(bidRequest.ortb2Imp, props)); +} + /** * Builds image asset request */ diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 4b0eebdf519..45498d2734a 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -17,6 +17,9 @@ describe('Adkernel adapter', function () { banner: { sizes: [[300, 250], [300, 200]] } + }, + ortb2Imp: { + battr: [6, 7, 9] } }, bid2_zone2 = { bidder: 'adkernel', @@ -95,12 +98,12 @@ describe('Adkernel adapter', function () { params: { zoneId: 1, host: 'rtb.adkernel.com', - video: {api: [1, 2]} }, mediaTypes: { video: { context: 'instream', - playerSize: [[640, 480]] + playerSize: [[640, 480]], + api: [1, 2] } }, adUnitCode: 'ad-unit-1' @@ -293,6 +296,7 @@ describe('Adkernel adapter', function () { describe('banner request building', function () { let bidRequest, bidRequests, _; + before(function () { [_, bidRequests] = buildRequest([bid1_zone1]); bidRequest = bidRequests[0]; @@ -337,6 +341,11 @@ describe('Adkernel adapter', function () { expect(bidRequest.device).to.have.property('dnt', 1); }); + it('should copy FPD to imp.banner', function() { + expect(bidRequest.imp[0].banner).to.have.property('battr'); + expect(bidRequest.imp[0].banner.battr).to.be.eql([6, 7, 9]); + }); + it('shouldn\'t contain gdpr nor ccpa information for default request', function () { let [_, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests[0]).to.not.have.property('regs'); From 47c8ead2fe1d51282000dfe1df55b1dfa92aa53b Mon Sep 17 00:00:00 2001 From: Jason Piros Date: Mon, 22 Aug 2022 15:21:33 -0700 Subject: [PATCH 173/569] consumableBidAdapter: remove impressionUrl (#8883) --- modules/consumableBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 6d502b24e81..87cb6052630 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -1,4 +1,4 @@ -import { logWarn, createTrackPixelHtml, deepAccess, isArray, deepSetValue } from '../src/utils.js'; +import { logWarn, deepAccess, isArray, deepSetValue } from '../src/utils.js'; import {config} from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; @@ -255,7 +255,7 @@ function getSize(sizes) { function retrieveAd(decision, unitId, unitName) { let ad; if (decision.contents && decision.contents[0]) { - ad = decision.contents[0].body + createTrackPixelHtml(decision.impressionUrl); + ad = decision.contents[0].body; } if (decision.vastXml) { ad = decision.vastXml; From 73c13cde704ef396a152ed9dcb8596779473107d Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 23 Aug 2022 09:03:58 -0400 Subject: [PATCH 174/569] Prebid Core: Add ttl buffer to videoCache.js (#8861) * Update videoCache.js * Update videoCache.js * Update videoCache_spec.js * Update videoCache_spec.js * master into ttl-buffer (#8869) * Update Sonobi adapter with GVLID (#8860) * dgkeyword RTD provider: fix tests causing ID5 test failures (#8862) Co-authored-by: Mike Miller Co-authored-by: Demetrio Girardi * Revert "master into ttl-buffer (#8869)" (#8879) This reverts commit b253980d38b6c801bc359066258b5988ce938865. * Name the constant Co-authored-by: Mike Miller Co-authored-by: Demetrio Girardi --- src/videoCache.js | 10 ++++++++-- test/spec/videoCache_spec.js | 14 +++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/videoCache.js b/src/videoCache.js index 219bca34726..f69a20f0139 100644 --- a/src/videoCache.js +++ b/src/videoCache.js @@ -13,6 +13,12 @@ import { ajax } from './ajax.js'; import { config } from './config.js'; import {auctionManager} from './auctionManager.js'; +/** + * Might be useful to be configurable in the future + * Depending on publisher needs + */ +const ttlBufferInSeconds = 15; + /** * @typedef {object} CacheableUrlBid * @property {string} vastUrl A URL which loads some valid VAST XML. @@ -63,11 +69,11 @@ function wrapURI(uri, impUrl) { function toStorageRequest(bid, {index = auctionManager.index} = {}) { const vastValue = bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl, bid.vastImpUrl); const auction = index.getAuction(bid); - + const ttlWithBuffer = Number(bid.ttl) + ttlBufferInSeconds; let payload = { type: 'xml', value: vastValue, - ttlseconds: Number(bid.ttl) + ttlseconds: ttlWithBuffer }; if (config.getConfig('cache.vasttrack')) { diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index fdb4103baed..5885dfb7cdf 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -155,12 +155,12 @@ describe('The video cache', function () { puts: [{ type: 'xml', value: vastXml1, - ttlseconds: 25, + ttlseconds: 40, key: customKey1 }, { type: 'xml', value: vastXml2, - ttlseconds: 25, + ttlseconds: 40, key: customKey2 }] }; @@ -205,7 +205,7 @@ describe('The video cache', function () { puts: [{ type: 'xml', value: vastXml1, - ttlseconds: 25, + ttlseconds: 40, key: customKey1, bidid: '12345abc', aid: '1234-56789-abcde', @@ -213,7 +213,7 @@ describe('The video cache', function () { }, { type: 'xml', value: vastXml2, - ttlseconds: 25, + ttlseconds: 40, key: customKey2, bidid: 'cba54321', aid: '1234-56789-abcde', @@ -276,7 +276,7 @@ describe('The video cache', function () { puts: [{ type: 'xml', value: vastXml1, - ttlseconds: 25, + ttlseconds: 40, key: customKey1, bidid: '12345abc', bidder: 'appnexus', @@ -285,7 +285,7 @@ describe('The video cache', function () { }, { type: 'xml', value: vastXml2, - ttlseconds: 25, + ttlseconds: 40, key: customKey2, bidid: 'cba54321', bidder: 'rubicon', @@ -309,7 +309,7 @@ describe('The video cache', function () { puts: [{ type: 'xml', value: expectedValue, - ttlseconds: 25 + ttlseconds: 40 }], }); } From 8d88902ff6f1479ac991d78d611e482f49c9c81c Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 23 Aug 2022 21:06:16 +0530 Subject: [PATCH 175/569] Added support to log extra bids from same bidder (#8886) Co-authored-by: pm-azhar-mulla --- modules/pubmaticAnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 38bcc4521be..c8dc7cef15d 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -382,7 +382,7 @@ function bidResponseHandler(args) { return; } - if (bid.bidder && args.bidderCode && bid.bidder !== args.bidderCode) { + if ((bid.bidder && args.bidderCode && bid.bidder !== args.bidderCode) || (bid.bidder === args.bidderCode && bid.status === SUCCESS)) { bid = copyRequiredBidDetails(args); cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId].push(bid); } From 07232521ed1d8185adc99a25a4eb4c266281d756 Mon Sep 17 00:00:00 2001 From: Ignat Khaylov Date: Wed, 24 Aug 2022 11:23:30 +0300 Subject: [PATCH 176/569] BetweenBidAdapter: default value for the cur parameter (#8870) * BetweenBidAdapter: default value for the cur parameter * fix linting Co-authored-by: Ignat Khaylov Co-authored-by: Chris Huie --- modules/betweenBidAdapter.js | 6 +++--- test/spec/modules/betweenBidAdapter_spec.js | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 2ca829b796b..ea28420481d 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -59,9 +59,9 @@ export const spec = { if (i.params.itu !== undefined) { params.itu = i.params.itu; } - if (i.params.cur !== undefined) { - params.cur = i.params.cur; - } + + params.cur = i.params.cur || 'USD'; + if (i.params.subid !== undefined) { params.subid = i.params.subid; } diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js index 3baa92e35d5..a4b89ab1b65 100644 --- a/test/spec/modules/betweenBidAdapter_spec.js +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -85,6 +85,23 @@ describe('betweenBidAdapterTests', function () { expect(req_data.cur).to.equal('THX'); }); + it('validate default cur USD', function() { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'between', + params: { + w: 240, + h: 400, + s: 1112 + }, + sizes: [[240, 400]] + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = JSON.parse(request.data)[0].data; + + expect(req_data.cur).to.equal('USD'); + }); it('validate subid param', function() { let bidRequestData = [{ bidId: 'bid1234', From 8a82e0935d7797513537e3f081a6f7afa0de0bdb Mon Sep 17 00:00:00 2001 From: BaronJHYu <254878848@qq.com> Date: Wed, 24 Aug 2022 22:17:01 +0800 Subject: [PATCH 177/569] Mediago Bid Adapter: initial adapter release (#8856) * Mediago Bid Adapter:new adapter * remove console * change spec file to fix CircleCI * change spec file to fix CircleCI * change spec file * Update mediagoBidAdapter.js * Update mediagoBidAdapter.js * rerun CurcleCi Co-authored-by: BaronYu --- modules/mediagoBidAdapter.js | 462 ++++++++++++++++++++ modules/mediagoBidAdapter.md | 34 ++ test/spec/modules/mediagoBidAdapter_spec.js | 97 ++++ 3 files changed, 593 insertions(+) create mode 100644 modules/mediagoBidAdapter.js create mode 100644 modules/mediagoBidAdapter.md create mode 100644 test/spec/modules/mediagoBidAdapter_spec.js diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js new file mode 100644 index 00000000000..28ca4e0bc50 --- /dev/null +++ b/modules/mediagoBidAdapter.js @@ -0,0 +1,462 @@ +/** + * gulp serve --modules=mediagoBidAdapter,pubCommonId --nolint --notest + */ + +import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +// import { config } from '../src/config.js'; +// import { isPubcidEnabled } from './pubCommonId.js'; + +const BIDDER_CODE = 'mediago'; +// const PROTOCOL = window.document.location.protocol; +const ENDPOINT_URL = + // ((PROTOCOL === 'https:') ? 'https' : 'http') + + 'https://rtb-us.mediago.io/api/bid?tn='; +const TIME_TO_LIVE = 500; +// const ENDPOINT_URL = '/api/bid?tn='; +const storage = getStorageManager(); +let globals = {}; +let itemMaps = {}; + +/** + * 获取随机id + * @param {number} a random number from 0 to 15 + * @return {string} random number or random string + */ +// function getRandomId( +// a // placeholder +// ) { +// // if the placeholder was passed, return +// // a random number from 0 to 15 +// return a +// ? ( +// a ^ // unless b is 8, +// ((Math.random() * // in which case +// 16) >> // a random number from +// (a / 4)) +// ) // 8 to 11 +// .toString(16) // in hexadecimal +// : ( // or otherwise a concatenated string: +// [1e7] + // 10000000 + +// 1e3 + // -1000 + +// 4e3 + // -4000 + +// 8e3 + // -80000000 + +// 1e11 +// ) // -100000000000, +// .replace( +// // replacing +// /[018]/g, // zeroes, ones, and eights with +// getRandomId // random hex digits +// ); +// } + +/* ----- mguid:start ------ */ +const COOKIE_KEY_MGUID = '__mguid_'; + +/** + * 获取用户id + * @return {string} + */ +const getUserID = () => { + const i = storage.getCookie(COOKIE_KEY_MGUID); + + if (i === null) { + const uuid = utils.generateUUID(); + storage.setCookie(COOKIE_KEY_MGUID, uuid); + return uuid; + } + return i; +}; + +/* ----- mguid:end ------ */ + +/** + * 获取一个对象的某个值,如果没有则返回空字符串 + * @param {Object} obj 对象 + * @param {...string} keys 键名 + * @return {any} + */ +function getProperty(obj, ...keys) { + let o = obj; + + for (let key of keys) { + // console.log(key, o); + if (o && o[key]) { + o = o[key]; + } else { + return ''; + } + } + return o; +} + +/** + * 是不是移动设备或者平板 + * @return {boolean} + */ +function isMobileAndTablet() { + let check = false; + (function (a) { + let reg1 = new RegExp( + [ + '(android|bbd+|meego)', + '.+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)', + '|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone', + '|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap', + '|windows ce|xda|xiino|android|ipad|playbook|silk', + ].join(''), + 'i' + ); + let reg2 = new RegExp( + [ + '1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)', + '|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )', + '|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell', + '|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)', + '|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene', + '|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c', + '|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom', + '|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)', + '|le(no|xi)|lg( g|/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)', + '|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]', + '|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)', + '|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio', + '|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms', + '|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al', + '|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)', + '|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|', + 'v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)', + '|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-', + '|your|zeto|zte-', + ].join(''), + 'i' + ); + if (reg1.test(a) || reg2.test(a.substr(0, 4))) { + check = true; + } + })(navigator.userAgent || navigator.vendor || window.opera); + return check; +} + +/** + * 获取底价 + * @param {*} bid + * @param {*} mediaType + * @param {*} sizes + * @returns + */ +// function getBidFloor(bid, mediaType, sizes) { +// var floor; +// var size = sizes.length === 1 ? sizes[0] : "*"; +// if (typeof bid.getFloor === "function") { +// const floorInfo = bid.getFloor({ currency: "USD", mediaType, size }); +// if ( +// typeof floorInfo === "object" && +// floorInfo.currency === "USD" && +// !isNaN(parseFloat(floorInfo.floor)) +// ) { +// floor = parseFloat(floorInfo.floor); +// } +// } +// return floor; +// } +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return utils.deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (_) { + return 0; + } +} + +/** + * 将尺寸转为RTB识别的尺寸 + * + * @param {Array|Object} requestSizes 配置尺寸 + * @return {Object} + */ +function transformSizes(requestSizes) { + let sizes = []; + let sizeObj = {}; + + if ( + utils.isArray(requestSizes) && + requestSizes.length === 2 && + !utils.isArray(requestSizes[0]) + ) { + sizeObj.width = parseInt(requestSizes[0], 10); + sizeObj.height = parseInt(requestSizes[1], 10); + sizes.push(sizeObj); + } else if (typeof requestSizes === 'object') { + for (let i = 0; i < requestSizes.length; i++) { + let size = requestSizes[i]; + sizeObj = {}; + sizeObj.width = parseInt(size[0], 10); + sizeObj.height = parseInt(size[1], 10); + sizes.push(sizeObj); + } + } + + return sizes; +} + +// 支持的广告尺寸 +const mediagoAdSize = [ + { w: 300, h: 250 }, + { w: 300, h: 600 }, + { w: 728, h: 90 }, + { w: 970, h: 250 }, + { w: 320, h: 50 }, + { w: 160, h: 600 }, + { w: 320, h: 180 }, + { w: 320, h: 100 }, + { w: 336, h: 280 }, +]; + +/** + * 获取广告位配置 + * @param {Array} validBidRequests an an array of bids + * @param {Object} bidderRequest The master bidRequest object + * @return {Object} + */ +function getItems(validBidRequests, bidderRequest) { + let items = []; + items = validBidRequests.map((req, i) => { + let ret = {}; + let mediaTypes = getProperty(req, 'mediaTypes'); + + let sizes = transformSizes(getProperty(req, 'sizes')); + let matchSize; + + // 确认尺寸是否符合我们要求 + for (let size of sizes) { + matchSize = mediagoAdSize.find( + (item) => size.width === item.w && size.height === item.h + ); + if (matchSize) { + break; + } + } + if (!matchSize) { + return {}; + } + + const bidFloor = getBidFloor(req); + // const gpid = + // utils.deepAccess(req, 'ortb2Imp.ext.gpid') || + // utils.deepAccess(req, 'ortb2Imp.ext.data.pbadslot') || + // utils.deepAccess(req, 'params.placementId', 0); + // console.log("wjh getItems:", req, bidFloor, gpid); + + // if (mediaTypes.native) {} + // banner广告类型 + if (mediaTypes.banner) { + let id = '' + (i + 1); + ret = { + id: id, + bidfloor: bidFloor, + banner: { + h: matchSize.h, + w: matchSize.w, + pos: 1, + }, + ext: { + // gpid: gpid, // 加入后无法返回广告 + }, + }; + itemMaps[id] = { + req, + ret, + }; + } + + return ret; + }); + return items; +} + +/** + * 获取rtb请求参数 + * + * @param {Array} validBidRequests an an array of bids + * @param {Object} bidderRequest The master bidRequest object + * @return {Object} + */ +function getParam(validBidRequests, bidderRequest) { + const pubcid = utils.deepAccess(validBidRequests[0], 'crumbs.pubcid'); + // console.log('wjh getParam', validBidRequests, bidderRequest); + let isMobile = isMobileAndTablet() ? 1 : 0; + let isTest = 0; + let auctionId = getProperty(bidderRequest, 'auctionId'); + let items = getItems(validBidRequests, bidderRequest); + + const domain = document.domain; + const location = utils.deepAccess(bidderRequest, 'refererInfo.referer'); + + const timeout = bidderRequest.timeout || 2000; + + if (items && items.length) { + let c = { + id: 'mgprebidjs_' + auctionId, + test: +isTest, + at: 1, + cur: ['USD'], + device: { + connectiontype: 0, + // ip: '64.188.178.115', + js: 1, + // language: "en", + // os: "Microsoft Windows", + // ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19043", + os: navigator.platform || '', + ua: navigator.userAgent, + language: /en/.test(navigator.language) ? 'en' : navigator.language, + }, + ext: {}, + user: { + buyeruid: getUserID(), + id: pubcid, + }, + site: { + name: domain, + domain: domain, + page: location, + ref: location, + mobile: isMobile, + cat: [], // todo + publisher: { + // todo + id: domain, + name: domain, + }, + }, + imp: items, + tmax: timeout, + }; + return c; + } else { + return null; + } +} + +export const spec = { + code: BIDDER_CODE, + // aliases: ['ex'], // short code + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + // console.log('mediago', { + // bid + // }); + if (bid.params.token) { + globals['token'] = bid.params.token; + } + return !!bid.params.token; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {Array} validBidRequests an an array of bids + * @param {Object} bidderRequest The master bidRequest object + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + let payload = getParam(validBidRequests, bidderRequest); + + const payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: ENDPOINT_URL + globals['token'], + data: payloadString, + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const bids = getProperty(serverResponse, 'body', 'seatbid', 0, 'bid'); + const cur = getProperty(serverResponse, 'body', 'cur'); + + const bidResponses = []; + for (let bid of bids) { + let impid = getProperty(bid, 'impid'); + if (itemMaps[impid]) { + let bidId = getProperty(itemMaps[impid], 'req', 'bidId'); + const bidResponse = { + requestId: bidId, + cpm: getProperty(bid, 'price'), + width: getProperty(bid, 'w'), + height: getProperty(bid, 'h'), + creativeId: getProperty(bid, 'crid'), + dealId: '', + currency: cur, + netRevenue: true, + ttl: TIME_TO_LIVE, + // referrer: REFERER, + ad: getProperty(bid, 'adm'), + nurl: getProperty(bid, 'nurl'), + // adserverTargeting: { + // granularityMultiplier: 0.1, + // priceGranularity: "pbHg", + // pbMg: "0.01", + // }, + // pbMg: "0.01", + // granularityMultiplier: 0.1, + // priceGranularity: "pbHg", + }; + bidResponses.push(bidResponse); + } + } + + return bidResponses; + }, + + /** + * Register bidder specific code, which will execute if bidder timed out after an auction + * @param {data} Containing timeout specific data + */ + // onTimeout: function (data) { + // // console.log('onTimeout', data); + // // Bidder specifc code + // }, + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} The bid that won the auction + */ + onBidWon: function (bid) { + // console.log('onBidWon: ', bid, config.getConfig('priceGranularity')); + // Bidder specific code + if (bid['nurl']) { + utils.triggerPixel(bid['nurl']); + } + }, + + /** + * Register bidder specific code, which will execute when the adserver targeting has been set for a bid from this bidder + * @param {Bid} The bid of which the targeting has been set + */ +// onSetTargeting: function (bid) { +// // console.log('onSetTargeting', bid); +// // Bidder specific code +// }, +}; +registerBidder(spec); diff --git a/modules/mediagoBidAdapter.md b/modules/mediagoBidAdapter.md new file mode 100644 index 00000000000..464ea59f11c --- /dev/null +++ b/modules/mediagoBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +``` +Module Name: MediaGo Bidder Adapter +Module Type: Bidder Adapter +Maintainer: fangsimin@baidu.com +``` + +# Description + +Module that connects to MediaGo's demand sources + +# Test Parameters + +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: "mediago", + params: { + token: '' // required, send email to ext_mediago_am@baidu.com to get the corresponding token + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/mediagoBidAdapter_spec.js b/test/spec/modules/mediagoBidAdapter_spec.js new file mode 100644 index 00000000000..e77af544429 --- /dev/null +++ b/test/spec/modules/mediagoBidAdapter_spec.js @@ -0,0 +1,97 @@ +import { expect } from 'chai'; +import { spec } from 'modules/mediagoBidAdapter.js'; + +describe('mediago:BidAdapterTests', function () { + let bidRequestData = { + bidderCode: 'mediago', + auctionId: '7fae02a9-0195-472f-ba94-708d3bc2c0d9', + bidderRequestId: '4fec04e87ad785', + bids: [ + { + bidder: 'mediago', + params: { + token: '85a6b01e41ac36d49744fad726e3655d', + bidfloor: 0.01, + }, + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + adUnitCode: 'regular_iframe', + transactionId: '7b26fdae-96e6-4c35-a18b-218dda11397d', + sizes: [[300, 250]], + bidId: '54d73f19c9d47a', // todo + bidderRequestId: '4fec04e87ad785', // todo + auctionId: '883a346a-6d62-4adb-a600-0f3a869061d1', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + }, + ], + }; + let request = []; + + it('mediago:validate_pub_params', function () { + expect( + spec.isBidRequestValid({ + bidder: 'mediago', + params: { + token: ['85a6b01e41ac36d49744fad726e3655d'], + }, + }) + ).to.equal(true); + }); + + it('mediago:validate_generated_params', function () { + request = spec.buildRequests(bidRequestData.bids, bidRequestData); + let req_data = JSON.parse(request.data); + expect(req_data.imp).to.have.lengthOf(1); + }); + + it('mediago:validate_response_params', function () { + let adm = ""; + let temp = '%3Cscr'; + temp += 'ipt%3E'; + temp += '!function()%7B%22use%20strict%22%3Bfunction%20f(t)%7Breturn(f%3D%22function%22%3D%3Dtypeof%20Symbol%26%26%22symbol%22%3D%3Dtypeof%20Symbol.iterator%3Ffunction(t)%7Breturn%20typeof%20t%7D%3Afunction(t)%7Breturn%20t%26%26%22function%22%3D%3Dtypeof%20Symbol%26%26t.constructor%3D%3D%3DSymbol%26%26t!%3D%3DSymbol.prototype%3F%22symbol%22%3Atypeof%20t%7D)(t)%7Dfunction%20l(t)%7Bvar%20e%3D0%3Carguments.length%26%26void%200!%3D%3Dt%3Ft%3A%7B%7D%3Btry%7Be.random_t%3D(new%20Date).getTime()%2Cg(function(t)%7Bvar%20e%3D1%3Carguments.length%26%26void%200!%3D%3Darguments%5B1%5D%3Farguments%5B1%5D%3A%22%22%3Bif(%22object%22!%3D%3Df(t))return%20e%3Bvar%20n%3Dfunction(t)%7Bfor(var%20e%2Cn%3D%5B%5D%2Co%3D0%2Ci%3DObject.keys(t)%3Bo%3Ci.length%3Bo%2B%2B)e%3Di%5Bo%5D%2Cn.push(%22%22.concat(e%2C%22%3D%22).concat(t%5Be%5D))%3Breturn%20n%7D(t).join(%22%26%22)%2Co%3De.indexOf(%22%23%22)%2Ci%3De%2Ct%3D%22%22%3Breturn-1!%3D%3Do%26%26(i%3De.slice(0%2Co)%2Ct%3De.slice(o))%2Cn%26%26(i%26%26-1!%3D%3Di.indexOf(%22%3F%22)%3Fi%2B%3D%22%26%22%2Bn%3Ai%2B%3D%22%3F%22%2Bn)%2Ci%2Bt%7D(e%2C%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Flog%2Ftrack%22))%7Dcatch(t)%7B%7D%7Dfunction%20g(t%2Ce%2Cn)%7B(t%3Dt%3Ft.split(%22%3B%3B%3B%22)%3A%5B%5D).map(function(t)%7Btry%7B0%3C%3Dt.indexOf(%22%2Fapi%2Fbidder%2Ftrack%22)%26%26n%26%26(t%2B%3D%22%26inIframe%3D%22.concat(!(!self.frameElement%7C%7C%22IFRAME%22!%3Dself.frameElement.tagName)%7C%7Cwindow.frames.length!%3Dparent.frames.length%7C%7Cself!%3Dtop)%2Ct%2B%3D%22%26pos_x%3D%22.concat(n.left%2C%22%26pos_y%3D%22).concat(n.top%2C%22%26page_w%3D%22).concat(n.page_width%2C%22%26page_h%3D%22).concat(n.page_height))%7Dcatch(t)%7Bl(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1002%2Cpos_err_m%3At.toString()%7D)%7Dvar%20e%3Dnew%20Image%3Be.src%3Dt%2Ce.style.display%3D%22none%22%2Ce.style.visibility%3D%22hidden%22%2Ce.width%3D0%2Ce.height%3D0%2Cdocument.body.appendChild(e)%7D)%7Dvar%20d%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D101%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BITRACKER2%7D%22%2C%22%24%7BITRACKER3%7D%22%2C%22%24%7BITRACKER4%7D%22%2C%22%24%7BITRACKER5%7D%22%2C%22%24%7BITRACKER6%7D%22%5D%2Cp%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D104%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26format%3D%26crid%3Dff32b6f9b3bbc45c00b78b6674a2952e%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BVTRACKER2%7D%22%2C%22%24%7BVTRACKER3%7D%22%2C%22%24%7BVTRACKER4%7D%22%2C%22%24%7BVTRACKER5%7D%22%2C%22%24%7BVTRACKER6%7D%22%5D%2Cs%3D%22f9f2b1ef23fe2759c2cad0953029a94b%22%2Cn%3Ddocument.getElementById(%22mgcontainer-99afea272c2b0e8626489674ddb7a0bb%22)%3Bn%26%26function()%7Bvar%20a%3Dn.getElementsByClassName(%22mediago-placement-track%22)%3Bif(a%26%26a.length)%7Bvar%20t%2Ce%3Dfunction(t)%7Bvar%20e%2Cn%2Co%2Ci%2Cc%2Cr%3B%22object%22%3D%3D%3Df(r%3Da%5Bt%5D)%26%26(e%3Dfunction(t)%7Btry%7Bvar%20e%3Dt.getBoundingClientRect()%2Cn%3De%26%26e.top%7C%7C-1%2Co%3De%26%26e.left%7C%7C-1%2Ci%3Ddocument.body.scrollWidth%7C%7C-1%2Ce%3Ddocument.body.scrollHeight%7C%7C-1%3Breturn%7Btop%3An.toFixed(0)%2Cleft%3Ao.toFixed(0)%2Cpage_width%3Ai%2Cpage_height%3Ae%7D%7Dcatch(o)%7Breturn%20l(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1001%2Cpos_err_m%3Ao.toString()%7D)%2C%7Btop%3A%22-1%22%2Cleft%3A%22-1%22%2Cpage_width%3A%22-1%22%2Cpage_height%3A%22-1%22%7D%7D%7D(r)%2C(n%3Dd%5Bt%5D)%26%26g(n%2C0%2Ce)%2Co%3Dp%5Bt%5D%2Ci%3D!1%2C(c%3Dfunction()%7BsetTimeout(function()%7Bvar%20t%2Ce%3B!i%26%26(t%3Dr%2Ce%3Dwindow.innerHeight%7C%7Cdocument.documentElement.clientHeight%7C%7Cdocument.body.clientHeight%2C(t.getBoundingClientRect()%26%26t.getBoundingClientRect().top)%3C%3De-.75*(t.offsetHeight%7C%7Ct.clientHeight))%3F(i%3D!0%2Co%26%26g(o))%3Ac()%7D%2C500)%7D)())%7D%3Bfor(t%20in%20a)e(t)%7D%7D()%7D()'; + temp += '%3B%3C%2Fscri'; + temp += 'pt%3E'; + adm += decodeURIComponent(temp); + let serverResponse = { + body: { + id: 'mgprebidjs_0b6572fc-ceba-418f-b6fd-33b41ad0ac8a', + seatbid: [ + { + bid: [ + { + id: '6e28cfaf115a354ea1ad8e1304d6d7b8', + impid: '1', + price: 0.087581, + adm: adm, + cid: '1339145', + crid: 'ff32b6f9b3bbc45c00b78b6674a2952e', + w: 300, + h: 250, + }, + ], + }, + ], + cur: 'USD', + }, + }; + + let bids = spec.interpretResponse(serverResponse); + // console.log({ + // bids + // }); + expect(bids).to.have.lengthOf(1); + + let bid = bids[0]; + + expect(bid.creativeId).to.equal('ff32b6f9b3bbc45c00b78b6674a2952e'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.currency).to.equal('USD'); + }); +}); From 9d8de1d468f46cb7863bf7ecf86d601e038869dc Mon Sep 17 00:00:00 2001 From: Alexander <32703851+pro-nsk@users.noreply.github.com> Date: Wed, 24 Aug 2022 23:11:20 +0700 Subject: [PATCH 178/569] Alkimi Bid Adapter: add adUnitCode parameter to bidder (#8897) * Alkimi bid adapter * Alkimi bid adapter * Alkimi bid adapter * alkimi adapter * onBidWon change * sign utils * auction ID as bid request ID * unit test fixes * change maintainer info * Updated the ad unit params * features support added * transfer adUnitCode * transfer adUnitCode: test Co-authored-by: Alexander Bogdanov Co-authored-by: Kalidas Engaiahraj Co-authored-by: mihanikw2g <92710748+mihanikw2g@users.noreply.github.com> Co-authored-by: Nikulin Mikhail --- modules/alkimiBidAdapter.js | 3 ++- test/spec/modules/alkimiBidAdapter_spec.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/alkimiBidAdapter.js b/modules/alkimiBidAdapter.js index ac8b5af9533..fe5a050f436 100644 --- a/modules/alkimiBidAdapter.js +++ b/modules/alkimiBidAdapter.js @@ -31,7 +31,8 @@ export const spec = { bidFloor: bidRequest.params.bidFloor, width: sizes[0].width, height: sizes[0].height, - impMediaType: getFormatType(bidRequest) + impMediaType: getFormatType(bidRequest), + adUnitCode: bidRequest.adUnitCode }) bidIds.push(bidRequest.bidId) }) diff --git a/test/spec/modules/alkimiBidAdapter_spec.js b/test/spec/modules/alkimiBidAdapter_spec.js index c98c81b706e..1ae9bb56df4 100644 --- a/test/spec/modules/alkimiBidAdapter_spec.js +++ b/test/spec/modules/alkimiBidAdapter_spec.js @@ -6,6 +6,7 @@ const REQUEST = { 'bidId': '456', 'bidder': 'alkimi', 'sizes': [[300, 250]], + 'adUnitCode': 'bannerAdUnitCode', 'mediaTypes': { 'banner': { 'sizes': [[300, 250]] @@ -138,7 +139,7 @@ describe('alkimiBidAdapter', function () { expect(bidderRequest.data.requestId).to.equal('123') expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') expect(bidderRequest.data.schain).to.deep.contains({ver: '1.0', complete: 1, nodes: [{asi: 'alkimi-onboarding.com', sid: '00001', hp: 1}]}) - expect(bidderRequest.data.signRequest.bids).to.deep.contains({ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, bidFloor: 0.1, width: 300, height: 250, impMediaType: 'Banner' }) + expect(bidderRequest.data.signRequest.bids).to.deep.contains({ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, bidFloor: 0.1, width: 300, height: 250, impMediaType: 'Banner', adUnitCode: 'bannerAdUnitCode' }) expect(bidderRequest.data.signRequest.randomUUID).to.equal(undefined) expect(bidderRequest.data.bidIds).to.deep.contains('456') expect(bidderRequest.data.signature).to.equal(undefined) From d88091c7689c47f964d52ad1f8a23b9eea1f9fc3 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Wed, 24 Aug 2022 18:12:37 +0200 Subject: [PATCH 179/569] Tappx Bid Adapter: fix host info http regex (#8896) * Fix: creating host correctly when http or https are added from the beginning * Fix :: Changed double quotes for single quotes Co-authored-by: Jordi Arnau --- modules/tappxBidAdapter.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 3cd77e7b853..11aa6c76c76 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -484,9 +484,18 @@ export function _getHostInfo(validBidRequests) { domainInfo.domain = hostParam.split('/', 1)[0]; + let regexHostParamHttps = new RegExp(`^https:\/\/`); + let regexHostParamHttp = new RegExp(`^http:\/\/`); + let regexNewEndpoints = new RegExp(`^(vz.*|zz.*)\\.[a-z]{3}\\.tappx\\.com$`, 'i'); let regexClassicEndpoints = new RegExp(`^([a-z]{3}|testing)\\.[a-z]{3}\\.tappx\\.com$`, 'i'); + if (regexHostParamHttps.test(hostParam)) { + hostParam = hostParam.replace('https://', ''); + } else if (regexHostParamHttp.test(hostParam)) { + hostParam = hostParam.replace('http://', ''); + } + if (regexNewEndpoints.test(domainInfo.domain)) { domainInfo.newEndpoint = true; domainInfo.endpoint = domainInfo.domain.split('.', 1)[0] From 5362745c08efcd374767f566a8c9bb1758093789 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 24 Aug 2022 12:38:29 -0600 Subject: [PATCH 180/569] Prebid core: optimize getRefererInfo to run only once per page (#8864) --- src/refererDetection.js | 13 ++++++++++++- test/spec/modules/enrichmentFpdModule_spec.js | 3 ++- test/spec/modules/fpdModule_spec.js | 3 ++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/refererDetection.js b/src/refererDetection.js index 15c080f5c69..28da182c7ab 100644 --- a/src/refererDetection.js +++ b/src/refererDetection.js @@ -11,6 +11,8 @@ import { config } from './config.js'; import {logWarn} from './utils.js'; +let RI = new WeakMap(); + /** * Prepend a URL with the page's protocol (http/https), if necessary. */ @@ -252,10 +254,19 @@ export function detectReferer(win) { }; } - return refererInfo; + return function() { + if (!RI.has(win)) { + RI.set(win, Object.freeze(refererInfo())); + } + return RI.get(win); + } } /** * @type {function(): refererInfo} */ export const getRefererInfo = detectReferer(window); + +export function resetRefererInfo() { + RI = new WeakMap(); +} diff --git a/test/spec/modules/enrichmentFpdModule_spec.js b/test/spec/modules/enrichmentFpdModule_spec.js index 7d7e463c015..3cc18f952cc 100644 --- a/test/spec/modules/enrichmentFpdModule_spec.js +++ b/test/spec/modules/enrichmentFpdModule_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { getRefererInfo } from 'src/refererDetection.js'; +import {getRefererInfo, resetRefererInfo} from 'src/refererDetection.js'; import { processFpd, coreStorage } from 'modules/enrichmentFpdModule.js'; describe('the first party data enrichment module', function() { @@ -20,6 +20,7 @@ describe('the first party data enrichment module', function() { }); beforeEach(function() { + resetRefererInfo(); querySelectorStub = sinon.stub(window.top.document, 'querySelector'); querySelectorStub.withArgs("link[rel='canonical']").returns(canonical); querySelectorStub.withArgs("meta[name='keywords']").returns(keywords); diff --git a/test/spec/modules/fpdModule_spec.js b/test/spec/modules/fpdModule_spec.js index 498bed29243..cf2ad4afe6f 100644 --- a/test/spec/modules/fpdModule_spec.js +++ b/test/spec/modules/fpdModule_spec.js @@ -1,6 +1,6 @@ import {expect} from 'chai'; import {config} from 'src/config.js'; -import {getRefererInfo} from 'src/refererDetection.js'; +import {getRefererInfo, resetRefererInfo} from 'src/refererDetection.js'; import {processFpd, registerSubmodules, startAuctionHook, reset} from 'modules/fpdModule/index.js'; import * as enrichmentModule from 'modules/enrichmentFpdModule.js'; import * as validationModule from 'modules/validationFpdModule/index.js'; @@ -70,6 +70,7 @@ describe('the first party data module', function () { }); beforeEach(function() { + resetRefererInfo(); querySelectorStub = sinon.stub(window.top.document, 'querySelector'); querySelectorStub.withArgs("link[rel='canonical']").returns(canonical); querySelectorStub.withArgs("meta[name='keywords']").returns(keywords); From 66fc005e1deaeee3fc1132ee324aee6efba54183 Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Thu, 25 Aug 2022 03:52:32 -0700 Subject: [PATCH 181/569] Prebid Core: Batch Video Cache Requests feature (#8765) * batch video cache request integration * got rid of console log statement * removed video.html * addressed feedback * removed video file and uncommented test * reverted package-lock.json * retriggering circle ci * addressed changes * got rid of video html test file * removed unused methods from spec file * refactored auction.js and updated tests * updated test * removed video file * fixed test * moved logic where batch config options are referenced into the returned func of batchingCache * removed test video file * reverted a few other changes * moved batch config code outside of batchingCache func * always forget to remove this file after haha Co-authored-by: Jason Quaccia --- src/auction.js | 73 ++++++++++++++++++++++++++++-------- test/spec/videoCache_spec.js | 30 +++++++++++++++ 2 files changed, 88 insertions(+), 15 deletions(-) diff --git a/src/auction.js b/src/auction.js index b7deeccfd82..da805ae41dd 100644 --- a/src/auction.js +++ b/src/auction.js @@ -514,28 +514,71 @@ function tryAddVideoBid(auctionInstance, bidResponse, afterBidAdded, {index = au } } -export const callPrebidCache = hook('async', function(auctionInstance, bidResponse, afterBidAdded, videoMediaType) { - store([bidResponse], function (error, cacheIds) { - if (error) { - logWarn(`Failed to save to the video cache: ${error}. Video bid must be discarded.`); - - doCallbacksIfTimedout(auctionInstance, bidResponse); - } else { - if (cacheIds[0].uuid === '') { - logWarn(`Supplied video cache key was already in use by Prebid Cache; caching attempt was rejected. Video bid must be discarded.`); +const storeInCache = (batch) => { + store(batch.map(entry => entry.bidResponse), function (error, cacheIds) { + cacheIds.forEach((cacheId, i) => { + const { auctionInstance, bidResponse, afterBidAdded } = batch[i]; + if (error) { + logWarn(`Failed to save to the video cache: ${error}. Video bid must be discarded.`); doCallbacksIfTimedout(auctionInstance, bidResponse); } else { - bidResponse.videoCacheKey = cacheIds[0].uuid; + if (cacheId.uuid === '') { + logWarn(`Supplied video cache key was already in use by Prebid Cache; caching attempt was rejected. Video bid must be discarded.`); - if (!bidResponse.vastUrl) { - bidResponse.vastUrl = getCacheUrl(bidResponse.videoCacheKey); + doCallbacksIfTimedout(auctionInstance, bidResponse); + } else { + bidResponse.videoCacheKey = cacheId.uuid; + + if (!bidResponse.vastUrl) { + bidResponse.vastUrl = getCacheUrl(bidResponse.videoCacheKey); + } + addBidToAuction(auctionInstance, bidResponse); + afterBidAdded(); } - addBidToAuction(auctionInstance, bidResponse); - afterBidAdded(); } - } + }); }); +}; + +let batchSize, batchTimeout; +config.getConfig('cache', (cacheConfig) => { + batchSize = typeof cacheConfig.cache.batchSize === 'number' && cacheConfig.cache.batchSize > 0 + ? cacheConfig.cache.batchSize + : 1; + batchTimeout = typeof cacheConfig.cache.batchTimeout === 'number' && cacheConfig.cache.batchTimeout > 0 + ? cacheConfig.cache.batchTimeout + : 0; +}); + +export const batchingCache = (timeout = setTimeout, cache = storeInCache) => { + let batches = [[]]; + let debouncing = false; + const noTimeout = cb => cb(); + + return function(auctionInstance, bidResponse, afterBidAdded) { + const batchFunc = batchTimeout > 0 ? timeout : noTimeout; + if (batches[batches.length - 1].length >= batchSize) { + batches.push([]); + } + + batches[batches.length - 1].push({auctionInstance, bidResponse, afterBidAdded}); + + if (!debouncing) { + debouncing = true; + batchFunc(() => { + batches.forEach(cache); + batches = [[]]; + debouncing = false; + }, batchTimeout); + } + } +}; + +const batchAndStore = batchingCache(); + +export const callPrebidCache = hook('async', function(auctionInstance, bidResponse, afterBidAdded, videoMediaType) { + batchAndStore(auctionInstance, bidResponse, afterBidAdded); }, 'callPrebidCache'); // Postprocess the bids so that all the universal properties exist, no matter which bidder they came from. diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index 5885dfb7cdf..a13028c966a 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -4,6 +4,7 @@ import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr.js'; import {auctionManager} from '../../src/auctionManager.js'; import {AuctionIndex} from '../../src/auctionIndex.js'; +import { batchingCache } from '../../src/auction.js'; const should = chai.should(); @@ -297,6 +298,35 @@ describe('The video cache', function () { JSON.parse(request.requestBody).should.deep.equal(payload); }); + it('should wait the duration of the batchTimeout and pass the correct batchSize if batched requests are enabled in the config', () => { + const mockAfterBidAdded = function() {}; + let callback = null; + let mockTimeout = sinon.stub().callsFake((cb) => { callback = cb }); + + config.setConfig({ + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache', + batchSize: 3, + batchTimeout: 20 + } + }); + + let stubCache = sinon.stub(); + const batchAndStore = batchingCache(mockTimeout, stubCache); + for (let i = 0; i < 3; i++) { + batchAndStore({}, {}, mockAfterBidAdded); + } + + sinon.assert.calledOnce(mockTimeout); + sinon.assert.calledWith(mockTimeout, sinon.match.any, 20); + + const expectedBatch = [{ afterBidAdded: mockAfterBidAdded, auctionInstance: { }, bidResponse: { } }, { afterBidAdded: mockAfterBidAdded, auctionInstance: { }, bidResponse: { } }, { afterBidAdded: mockAfterBidAdded, auctionInstance: { }, bidResponse: { } }]; + + callback(); + + sinon.assert.calledWith(stubCache, expectedBatch); + }); + function assertRequestMade(bid, expectedValue) { store([bid], function () { }); From fb790f7fda84ca8332116d9e3a356e60da735d99 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 25 Aug 2022 13:36:20 +0000 Subject: [PATCH 182/569] Prebid 7.12.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a6ae1e5a3e8..6cd1eedebd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.12.0-pre", + "version": "7.12.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index a9a4c120c48..85c913baf21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.12.0-pre", + "version": "7.12.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 5a92113c4152b68bfb054bd472556c2e925cffc9 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 25 Aug 2022 13:36:20 +0000 Subject: [PATCH 183/569] Increment version to 7.13.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cd1eedebd1..160f2fbfe5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.12.0", + "version": "7.13.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 85c913baf21..5f68825a89d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.12.0", + "version": "7.13.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From fab2dc6ec49d3fbad635fc062679ea48db9aaecf Mon Sep 17 00:00:00 2001 From: AcuityAdsIntegrations <72594990+AcuityAdsIntegrations@users.noreply.github.com> Date: Thu, 25 Aug 2022 21:48:12 +0300 Subject: [PATCH 184/569] AcuityAds Bid Adapter: initial adapter release (#8854) --- modules/acuityAdsBidAdapter.js | 207 +++++++++ modules/acuityAdsBidAdapter.md | 79 ++++ test/spec/modules/acuityAdsBidAdapter_spec.js | 398 ++++++++++++++++++ 3 files changed, 684 insertions(+) create mode 100644 modules/acuityAdsBidAdapter.js create mode 100644 modules/acuityAdsBidAdapter.md create mode 100644 test/spec/modules/acuityAdsBidAdapter_spec.js diff --git a/modules/acuityAdsBidAdapter.js b/modules/acuityAdsBidAdapter.js new file mode 100644 index 00000000000..f469fe48c60 --- /dev/null +++ b/modules/acuityAdsBidAdapter.js @@ -0,0 +1,207 @@ +import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'acuityads'; +const AD_URL = 'https://prebid.admanmedia.com/pbjs'; +const SYNC_URL = 'https://cs.admanmedia.com'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl || bid.vastXml); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); + default: + return false; + } +} + +function getPlacementReqData(bid) { + const { params, bidId, mediaTypes } = bid; + const schain = bid.schain || {}; + const { placementId } = params; + const bidfloor = getBidFloor(bid); + + const placement = { + bidId, + schain, + bidfloor + }; + + placement.placementId = placementId; + placement.type = 'publisher'; + + if (mediaTypes && mediaTypes[BANNER]) { + placement.adFormat = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.adFormat = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.native = mediaTypes[NATIVE]; + placement.adFormat = NATIVE; + } + + return placement; +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (err) { + logError(err); + return 0; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid = {}) => { + const { params, bidId, mediaTypes } = bid; + let valid = Boolean(bidId && params && params.placementId); + + if (mediaTypes && mediaTypes[BANNER]) { + valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); + } else if (mediaTypes && mediaTypes[VIDEO]) { + valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); + } else if (mediaTypes && mediaTypes[NATIVE]) { + valid = valid && Boolean(mediaTypes[NATIVE]); + } else { + valid = false; + } + return valid; + }, + + buildRequests: (validBidRequests = [], bidderRequest = {}) => { + // convert Native ORTB definition to old-style prebid native definition + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + + let deviceWidth = 0; + let deviceHeight = 0; + + let winLocation; + try { + const winTop = window.top; + deviceWidth = winTop.screen.width; + deviceHeight = winTop.screen.height; + winLocation = winTop.location; + } catch (e) { + logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + logMessage(e); + } + // TODO: does the fallback make sense here? + let location = refferLocation || winLocation; + const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + const host = location.host; + const page = location.pathname; + const secure = location.protocol === 'https:' ? 1 : 0; + const placements = []; + const request = { + deviceWidth, + deviceHeight, + language, + secure, + host, + page, + placements, + coppa: config.getConfig('coppa') === true ? 1 : 0, + ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, + tmax: config.getConfig('bidderTimeout') + }; + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + placements.push(getPlacementReqData(bid)); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/acuityAdsBidAdapter.md b/modules/acuityAdsBidAdapter.md new file mode 100644 index 00000000000..a19e0a6b0ba --- /dev/null +++ b/modules/acuityAdsBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: AcuityAds Bidder Adapter +Module Type: AcuityAds Bidder Adapter +Maintainer: sa-support@brightcom.com +``` + +# Description + +Connects to AcuityAds exchange for bids. +AcuityAds bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'acuityads', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'acuityads', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'acuityads', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/acuityAdsBidAdapter_spec.js b/test/spec/modules/acuityAdsBidAdapter_spec.js new file mode 100644 index 00000000000..18ea574c1ce --- /dev/null +++ b/test/spec/modules/acuityAdsBidAdapter_spec.js @@ -0,0 +1,398 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/acuityAdsBidAdapter'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'acuityads' + +describe('AcuityAdsBidAdapter', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative', + } + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + refererInfo: { + referer: 'https://test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://prebid.admanmedia.com/pbjs'); + }); + + it('Returns general data valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('string'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([], bidderRequest); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.admanmedia.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1---' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.admanmedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') + }); + }); +}); From 2a162842da342b45edf0c633a741ee6b5fd5110b Mon Sep 17 00:00:00 2001 From: eknis Date: Fri, 26 Aug 2022 23:43:38 +0900 Subject: [PATCH 185/569] ImRtdProvider: add segment max (#8902) * imRtdProvider: add segment max param * imRtdProvider: refactoring * imRtdProvider: refactoring --- modules/imRtdProvider.js | 27 ++++++++++++++++++------- modules/imRtdProvider.md | 4 +++- test/spec/modules/imRtdProvider_spec.js | 15 +++++++++----- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/modules/imRtdProvider.js b/modules/imRtdProvider.js index 0ed239019cf..bc01896d062 100644 --- a/modules/imRtdProvider.js +++ b/modules/imRtdProvider.js @@ -37,28 +37,40 @@ function setImDataInCookie(value) { ); } +/** + * @param {Object} segments + * @param {Object} moduleConfig + */ +function getSegments(segments, moduleConfig) { + if (!segments) return; + const maxSegments = !Number.isNaN(moduleConfig.params.maxSegments) ? moduleConfig.params.maxSegments : 200; + return segments.slice(0, maxSegments); +} + /** * @param {string} bidderName */ export function getBidderFunction(bidderName) { const biddersFunction = { - pubmatic: function (bid, data) { + pubmatic: function (bid, data, moduleConfig) { if (data.im_segments && data.im_segments.length) { + const segments = getSegments(data.im_segments, moduleConfig); const dctr = deepAccess(bid, 'params.dctr'); deepSetValue( bid, 'params.dctr', - `${dctr ? dctr + '|' : ''}im_segments=${data.im_segments.join(',')}` + `${dctr ? dctr + '|' : ''}im_segments=${segments.join(',')}` ); } return bid }, - fluct: function (bid, data) { + fluct: function (bid, data, moduleConfig) { if (data.im_segments && data.im_segments.length) { + const segments = getSegments(data.im_segments, moduleConfig); deepSetValue( bid, 'params.kv.imsids', - data.im_segments + segments ); } return bid @@ -88,14 +100,15 @@ export function setRealTimeData(bidConfig, moduleConfig, data) { const utils = {deepSetValue, deepAccess, logInfo, logError, mergeDeep}; if (data.im_segments) { + const segments = getSegments(data.im_segments, moduleConfig); const ortb2 = bidConfig.ortb2Fragments?.global || {}; - deepSetValue(ortb2, 'user.ext.data.im_segments', data.im_segments); + deepSetValue(ortb2, 'user.ext.data.im_segments', segments); if (moduleConfig.params.setGptKeyValues || !moduleConfig.params.hasOwnProperty('setGptKeyValues')) { window.googletag = window.googletag || {cmd: []}; window.googletag.cmd = window.googletag.cmd || []; window.googletag.cmd.push(() => { - window.googletag.pubads().setTargeting('im_segments', data.im_segments); + window.googletag.pubads().setTargeting('im_segments', segments); }); } } @@ -107,7 +120,7 @@ export function setRealTimeData(bidConfig, moduleConfig, data) { if (overwriteFunction) { overwriteFunction(bid, data, utils, config); } else if (bidderFunction) { - bidderFunction(bid, data); + bidderFunction(bid, data, moduleConfig); } }) }); diff --git a/modules/imRtdProvider.md b/modules/imRtdProvider.md index 7ece2b996b4..8f31d3eb545 100644 --- a/modules/imRtdProvider.md +++ b/modules/imRtdProvider.md @@ -21,7 +21,8 @@ pbjs.setConfig( waitForIt: true, params: { cid: 5126, // Set your Intimate Merger Customer ID here for production - setGptKeyValues: true + setGptKeyValues: true, + maxSegments: 200 // maximum number is 200 } } ] @@ -39,3 +40,4 @@ pbjs.setConfig( | params | Required | Object | Details of module params. | | | params.cid | Required | Number | This is the Customer ID value obtained via Intimate Merger. | `5126` | | params.setGptKeyValues | Optional | Boolean | This is set targeting for GPT/GAM. Default setting is true. | `true` | +| params.maxSegments | Optional | Number | This is set maximum number of rtd segments at once. Default setting is 200. | `200` | diff --git a/test/spec/modules/imRtdProvider_spec.js b/test/spec/modules/imRtdProvider_spec.js index b580e5b6516..89328b91529 100644 --- a/test/spec/modules/imRtdProvider_spec.js +++ b/test/spec/modules/imRtdProvider_spec.js @@ -27,7 +27,8 @@ describe('imRtdProvider', function () { const moduleConfig = { params: { cid: 5126, - setGptKeyValues: true + setGptKeyValues: true, + maxSegments: 2 } } @@ -60,11 +61,11 @@ describe('imRtdProvider', function () { it(`should return bid with correct key data: ${bidderName}`, function () { const bid = {bidder: bidderName}; - expect(getBidderFunction(bidderName)(bid, {'im_segments': ['12345', '67890']})).to.equal(bid); + expect(getBidderFunction(bidderName)(bid, {'im_segments': ['12345', '67890']}, {params: {}})).to.equal(bid); }); it(`should return bid without data: ${bidderName}`, function () { const bid = {bidder: bidderName}; - expect(getBidderFunction(bidderName)(bid, '')).to.equal(bid); + expect(getBidderFunction(bidderName)(bid, '', {params: {}})).to.equal(bid); }); }); it(`should return null with unexpected bidder`, function () { @@ -73,7 +74,7 @@ describe('imRtdProvider', function () { describe('fluct bidder function', function () { it('should return a bid w/o im_segments if not any exists', function () { const bid = {bidder: 'fluct'}; - expect(getBidderFunction('fluct')(bid, '')).to.eql(bid); + expect(getBidderFunction('fluct')(bid, '', {params: {}})).to.eql(bid); }); it('should return a bid w/ im_segments if any exists', function () { const bid = { @@ -84,7 +85,11 @@ describe('imRtdProvider', function () { } } }; - expect(getBidderFunction('fluct')(bid, {im_segments: ['12345', '67890']})) + expect(getBidderFunction('fluct')( + bid, + {im_segments: ['12345', '67890', '09876']}, + {params: {maxSegments: 2}} + )) .to.eql( { bidder: 'fluct', From cc3b3db43d435e0f73d5c5d494df792ec4703b31 Mon Sep 17 00:00:00 2001 From: probably-kira Date: Fri, 26 Aug 2022 11:28:40 -0400 Subject: [PATCH 186/569] Lifestreet Bid adapter: initial release (#8340) * Lifestreet adapter with meta support added * Added tests * fix linting error * Fixed typo in tests * CR fix: import functions from utils instead of using * Co-authored-by: Chris Huie --- modules/lifestreetBidAdapter.js | 143 +++++++++++ .../spec/modules/lifestreetBidAdapter_spec.js | 232 ++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 modules/lifestreetBidAdapter.js create mode 100644 test/spec/modules/lifestreetBidAdapter_spec.js diff --git a/modules/lifestreetBidAdapter.js b/modules/lifestreetBidAdapter.js new file mode 100644 index 00000000000..6a8b783ce21 --- /dev/null +++ b/modules/lifestreetBidAdapter.js @@ -0,0 +1,143 @@ +import { isInteger } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'lifestreet'; +const ADAPTER_VERSION = '$prebid.version$'; + +const urlTemplate = template`https://ads.lfstmedia.com/gate/${'adapter'}/${'slot'}?adkey=${'adkey'}&ad_size=${'ad_size'}&__location=${'location'}&__referrer=${'referrer'}&__wn=${'wn'}&__sf=${'sf'}&__fif=${'fif'}&__if=${'if'}&__stamp=${'stamp'}&__pp=1&__hb=1&_prebid_json=1&__gz=1&deferred_format=vast_2_0,vast_3_0&__hbver=${'hbver'}`; + +/** + * A helper function for template to generate string from boolean + */ +function boolToString(value) { + return value ? '1' : '0'; +} + +/** + * A helper function to form URL from the template + */ +function template(strings, ...keys) { + return function(...values) { + let dict = values[values.length - 1] || {}; + let result = [strings[0]]; + keys.forEach(function(key, i) { + let value = isInteger(key) ? values[key] : dict[key]; + result.push(value, strings[i + 1]); + }); + return result.join(''); + }; +} + +/** + * Creates a bid requests for a given bid. + * + * @param {BidRequest} bid The bid params to use for formatting a request + */ +function formatBidRequest(bid, bidderRequest = {}) { + const {params} = bid; + const {referer} = (bidderRequest.refererInfo || {}); + let url = urlTemplate({ + adapter: 'prebid', + slot: params.slot, + adkey: params.adkey, + ad_size: params.ad_size, + location: referer, + referrer: referer, + wn: boolToString(/fb_http/i.test(window.name)), + sf: boolToString(window['sfAPI'] || window['$sf']), + fif: boolToString(window['inDapIF'] === true), + if: boolToString(window !== window.top), + stamp: new Date().getTime(), + hbver: ADAPTER_VERSION + }); + + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.gdprApplies !== undefined) { + const gdpr = '&__gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? '1' : '0'); + url += gdpr; + } + if (bidderRequest.gdprConsent.consentString !== undefined) { + url += `&__consent=${bidderRequest.gdprConsent.consentString}`; + } + } + + // ccpa support + if (bidderRequest.uspConsent) { + url += `&__us_privacy=${bidderRequest.uspConsent}` + } + + return { + method: 'GET', + url: url, + bidId: bid.bidId + }; +} + +function isResponseValid(response) { + return !/^\s*\{\s*"advertisementAvailable"\s*:\s*false/i.test(response.content) && + response.content.indexOf('') === -1 && /* (typeof response.cpm !== 'undefined') && */ + response.status === 1; +} + +export const spec = { + code: BIDDER_CODE, + aliases: ['lsm'], + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: (bid = {}) => { + const {params = {}} = bid; + return !!(params.slot && params.adkey && params.ad_size); + }, + + buildRequests: (validBidRequests, bidderRequest) => { + return validBidRequests.map(bid => { + return formatBidRequest(bid, bidderRequest) + }); + }, + + interpretResponse: (serverResponse, bidRequest) => { + const bidResponses = []; + let response = serverResponse.body; + if (!isResponseValid(response)) { + return bidResponses; + } + + const isVideo = response.content_type.indexOf('vast') > -1; + const mediaType = isVideo ? VIDEO : BANNER; + + const bidResponse = { + requestId: bidRequest.bidId, + cpm: response.cpm, + currency: response.currency ? response.currency : 'USD', + width: response.width, + height: response.height, + creativeId: response.creativeId, + netRevenue: response.netRevenue ? response.netRevenue : true, + ttl: response.ttl ? response.ttl : 86400, + mediaType, + meta: { + mediaType, + advertiserDomains: response.advertiserDomains + } + }; + + if (response.hasOwnProperty('dealId')) { + bidResponse.dealId = response.dealId; + } + if (isVideo) { + if (typeof response.vastUrl !== 'undefined') { + bidResponse.vastUrl = response.vastUrl; + } else { + bidResponse.vastXml = response.content; + } + } else { + bidResponse.ad = response.content; + } + + bidResponses.push(bidResponse); + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/test/spec/modules/lifestreetBidAdapter_spec.js b/test/spec/modules/lifestreetBidAdapter_spec.js new file mode 100644 index 00000000000..d66727da644 --- /dev/null +++ b/test/spec/modules/lifestreetBidAdapter_spec.js @@ -0,0 +1,232 @@ +import { expect } from 'chai'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import { spec } from 'modules/lifestreetBidAdapter.js'; + +describe('lifestreetBidAdapter', function() { + let bidRequests; + let videoBidRequests; + let bidResponses; + let videoBidResponses; + beforeEach(function() { + bidRequests = [ + { + bidder: 'lifestreet', + params: { + slot: 'slot166704', + adkey: '78c', + ad_size: '160x600' + }, + mediaTypes: { + banner: { + sizes: [ + [160, 600], + [300, 600] + ] + } + }, + sizes: [ + [160, 600], + [300, 600] + ] + } + ]; + + bidResponses = { + body: { + cpm: 0.1, + netRevenue: true, + content_type: 'display_flash', + width: 160, + currency: 'USD', + ttl: 86400, + content: '', + 'price': 0.8, + 'creativeId': '12610997325162499419', + 'exp': 30, + 'width': 300, + 'height': 250, + 'advertiserDomains': ['securepubads.g.doubleclick.net'], + 'cookies': [{ + 'src': 'https://sync.com', + 'type': 'iframe' + }, { + 'src': 'https://sync.com', + 'type': 'img' + }] + }] + } +}; + +const REQUEST = { + data: { + width: 300, + height: 250, + bidId: '2d52001cabd527' + } +}; + +function getTopWindowQueryParams() { + try { + const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + return parsedUrl.search; + } catch (e) { + return ''; + } +} + +describe('KueezRtbBidAdapter', function () { + describe('validtae spec', function () { + it('exists and is a function', function () { + expect(adapter.isBidRequestValid).to.exist.and.to.be.a('function'); + }); + + it('exists and is a function', function () { + expect(adapter.buildRequests).to.exist.and.to.be.a('function'); + }); + + it('exists and is a function', function () { + expect(adapter.interpretResponse).to.exist.and.to.be.a('function'); + }); + + it('exists and is a function', function () { + expect(adapter.getUserSyncs).to.exist.and.to.be.a('function'); + }); + + it('exists and is a string', function () { + expect(adapter.code).to.exist.and.to.be.a('string'); + }); + }); + + describe('validate bid requests', function () { + it('should require cId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + pId: 'pid' + } + }); + expect(isValid).to.be.false; + }); + + it('should require pId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid' + } + }); + expect(isValid).to.be.false; + }); + + it('should validate correctly', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid', + pId: 'pid' + } + }); + expect(isValid).to.be.true; + }); + }); + + describe('build requests', function () { + let sandbox; + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + kueezrtb: { + storageAllowed: true + } + }; + sandbox = sinon.sandbox.create(); + sandbox.stub(Date, 'now').returns(1000); + }); + + it('should build request for each size', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + const requests = adapter.buildRequests([BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/59db6b3b4ffaa70004f45cdc`, + data: { + gdprConsent: 'consent_string', + gdpr: 1, + usPrivacy: 'consent_string', + sizes: ['300x250', '300x600'], + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + cb: 1000, + bidFloor: 0.1, + bidId: '2d52001cabd527', + adUnitCode: 'div-gpt-ad-12345-0', + publisherId: '59ac17c192832d0011283fe3', + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + bidderVersion: adapter.version, + prebidVersion: version, + schain: BID.schain, + res: `${window.top.screen.width}x${window.top.screen.height}`, + uqs: getTopWindowQueryParams(), + 'ext.param1': 'loremipsum', + 'ext.param2': 'dolorsitamet', + } + }); + }); + + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + sandbox.restore(); + }); + }); + describe('getUserSyncs', function () { + it('should have valid user sync with iframeEnabled', function () { + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.kueezrtb.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' + }]); + }); + + it('should have valid user sync with cid on response', function () { + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.kueezrtb.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' + }]); + }); + + it('should have valid user sync with pixelEnabled', function () { + const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + 'url': 'https://sync.kueezrtb.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', + 'type': 'image' + }]); + }) + }); + + describe('interpret response', function () { + it('should return empty array when there is no response', function () { + const responses = adapter.interpretResponse(null); + expect(responses).to.be.empty; + }); + + it('should return empty array when there is no ad', function () { + const responses = adapter.interpretResponse({price: 1, ad: ''}); + expect(responses).to.be.empty; + }); + + it('should return empty array when there is no price', function () { + const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + expect(responses).to.be.empty; + }); + + it('should return an array of interpreted responses', function () { + const responses = adapter.interpretResponse(SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 0.8, + width: 300, + height: 250, + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 30, + ad: '', + meta: { + advertiserDomains: ['securepubads.g.doubleclick.net'] + } + }); + }); + + it('should take default TTL', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + delete serverResponse.body.results[0].exp; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0].ttl).to.equal(300); + }); + }); + + describe('user id system', function () { + Object.keys(SUPPORTED_ID_SYSTEMS).forEach((idSystemProvider) => { + const id = Date.now().toString(); + const bid = utils.deepClone(BID); + + const userId = (function () { + switch (idSystemProvider) { + case 'lipb': + return {lipbid: id}; + case 'parrableId': + return {eid: id}; + case 'id5id': + return {uid: id}; + default: + return id; + } + })(); + + bid.userId = { + [idSystemProvider]: userId + }; + + it(`should include 'uid.${idSystemProvider}' in request params`, function () { + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data[`uid.${idSystemProvider}`]).to.equal(id); + }); + }); + }); + + describe('alternate param names extractors', function () { + it('should return undefined when param not supported', function () { + const cid = extractCID({'c_id': '1'}); + const pid = extractPID({'p_id': '1'}); + const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + expect(cid).to.be.undefined; + expect(pid).to.be.undefined; + expect(subDomain).to.be.undefined; + }); + + it('should return value when param supported', function () { + const cid = extractCID({'cID': '1'}); + const pid = extractPID({'Pid': '2'}); + const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + expect(cid).to.be.equal('1'); + expect(pid).to.be.equal('2'); + expect(subDomain).to.be.equal('prebid'); + }); + }); + + describe('unique deal id', function () { + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + kueezrtb: { + storageAllowed: true + } + }; + }); + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + }); + const key = 'myKey'; + let uniqueDealId; + beforeEach(() => { + uniqueDealId = getUniqueDealId(key, 0); + }) + + it('should get current unique deal id', function (done) { + // waiting some time so `now` will become past + setTimeout(() => { + const current = getUniqueDealId(key); + expect(current).to.be.equal(uniqueDealId); + done(); + }, 200); + }); + + it('should get new unique deal id on expiration', function (done) { + setTimeout(() => { + const current = getUniqueDealId(key, 100); + expect(current).to.not.be.equal(uniqueDealId); + done(); + }, 200) + }); + }); + + describe('storage utils', function () { + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + kueezrtb: { + storageAllowed: true + } + }; + }); + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + }); + it('should get value from storage with create param', function () { + const now = Date.now(); + const clock = useFakeTimers({ + shouldAdvanceTime: true, + now + }); + setStorageItem('myKey', 2020); + const {value, created} = getStorageItem('myKey'); + expect(created).to.be.equal(now); + expect(value).to.be.equal(2020); + expect(typeof value).to.be.equal('number'); + expect(typeof created).to.be.equal('number'); + clock.restore(); + }); + + it('should get external stored value', function () { + const value = 'superman' + window.localStorage.setItem('myExternalKey', value); + const item = getStorageItem('myExternalKey'); + expect(item).to.be.equal(value); + }); + + it('should parse JSON value', function () { + const data = JSON.stringify({event: 'send'}); + const {event} = tryParseJSON(data); + expect(event).to.be.equal('send'); + }); + + it('should get original value on parse fail', function () { + const value = 21; + const parsed = tryParseJSON(value); + expect(typeof parsed).to.be.equal('number'); + expect(parsed).to.be.equal(value); + }); + }); +}); From f7bba1dfa44e3d19a04fbd377bcb38ac6599a825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Thu, 3 Nov 2022 20:13:11 +0200 Subject: [PATCH 406/569] added gvlid code (#9199) --- modules/kueezRtbBidAdapter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/kueezRtbBidAdapter.js b/modules/kueezRtbBidAdapter.js index 55545a8092d..1ee3b7331cb 100644 --- a/modules/kueezRtbBidAdapter.js +++ b/modules/kueezRtbBidAdapter.js @@ -3,6 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; +const GVLID = 1165; const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'kueezrtb'; const BIDDER_VERSION = '1.0.0'; @@ -21,7 +22,7 @@ export const SUPPORTED_ID_SYSTEMS = { 'tdid': 1, 'pubProvidedId': 1 }; -const storage = getStorageManager({ gvlid: null, bidderCode: BIDDER_CODE }); +const storage = getStorageManager({ gvlid: GVLID, bidderCode: BIDDER_CODE }); function getTopWindowQueryParams() { try { @@ -266,6 +267,7 @@ export function tryParseJSON(value) { export const spec = { code: BIDDER_CODE, version: BIDDER_VERSION, + gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid, buildRequests, From 530017f8d293ad7ae651edf238e92c661899680c Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Thu, 3 Nov 2022 11:17:25 -0700 Subject: [PATCH 407/569] updated docs for adRenderSucceeded event (#9195) --- src/adRendering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adRendering.js b/src/adRendering.js index a645ec77244..0a847d7cc25 100644 --- a/src/adRendering.js +++ b/src/adRendering.js @@ -23,7 +23,7 @@ export function emitAdRenderFail({ reason, message, bid, id }) { /** * Emit the AD_RENDER_SUCCEEDED event. - * + * (Note: Invocation of this function indicates that the render function did not generate an error, it does not guarantee that tracking for this event has occurred yet.) * @param doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in * a cross-origin frame). * @param bid bid response object for the ad that was rendered From 338b9c671f293f07742b647135de3e352d982e5b Mon Sep 17 00:00:00 2001 From: John Ivan Bauzon Date: Fri, 4 Nov 2022 02:19:33 +0800 Subject: [PATCH 408/569] gumgum Bid Adapter: support coppa config (#9192) * ADJS-1227-add-coppa-flag-to-gumgum-adapter * added tests for coppa config for gumgumBidAdapter Co-authored-by: John Bauzon --- modules/gumgumBidAdapter.js | 4 ++++ test/spec/modules/gumgumBidAdapter_spec.js | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index fe15602c0c0..93306984ab1 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -294,6 +294,7 @@ function buildRequests(validBidRequests, bidderRequest) { const gdprConsent = bidderRequest && bidderRequest.gdprConsent; const uspConsent = bidderRequest && bidderRequest.uspConsent; const timeout = config.getConfig('bidderTimeout'); + const coppa = config.getConfig('coppa') === true ? 1 : 0; const topWindowUrl = bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.page; _each(validBidRequests, bidRequest => { const { @@ -386,6 +387,9 @@ function buildRequests(validBidRequests, bidderRequest) { if (uspConsent) { data.uspConsent = uspConsent; } + if (coppa) { + data.coppa = coppa; + } if (schain && schain.nodes) { data.schain = _serializeSupplyChainObj(schain); } diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 17fff31f132..a2dacb16b73 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -1,5 +1,6 @@ import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import { config } from 'src/config.js'; import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec } from 'modules/gumgumBidAdapter.js'; @@ -484,6 +485,20 @@ describe('gumgumAdapter', function () { const bidRequest = spec.buildRequests(bidRequests, fakeBidRequest)[0]; expect(bidRequest.data).to.not.include.any.keys('gdprConsent') }); + it('should not set coppa parameter if coppa config is set to false', function () { + config.setConfig({ + coppa: false + }); + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.coppa).to.eq(undefined); + }); + it('should set coppa parameter to 1 if coppa config is set to true', function () { + config.setConfig({ + coppa: true + }); + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.coppa).to.eq(1); + }); it('should add uspConsent parameter if it is present in the bidderRequest', function () { const noUspBidRequest = spec.buildRequests(bidRequests)[0]; const uspConsentObj = { uspConsent: '1YYY' }; From 7a46ed1e9b79224dfa28832b74349984cae9429f Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 3 Nov 2022 12:40:44 -0600 Subject: [PATCH 409/569] Dependencies : update outdated dependencies and automated security alerts (#9172) * update package-lock * Fix wdio version Co-authored-by: Demetrio Girardi --- package-lock.json | 16356 +++++++++++++++++++++++++++++--------------- package.json | 14 +- 2 files changed, 10662 insertions(+), 5708 deletions(-) diff --git a/package-lock.json b/package-lock.json index d37403f06e1..7567271b07f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "7.23.0-pre", + "version": "7.24.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -26,13 +26,13 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", - "@wdio/browserstack-service": "^7.16.0", - "@wdio/cli": "^7.5.2", - "@wdio/concise-reporter": "^7.5.2", - "@wdio/local-runner": "^7.5.2", - "@wdio/mocha-framework": "^7.5.2", - "@wdio/spec-reporter": "^7.19.0", - "@wdio/sync": "^7.5.2", + "@wdio/browserstack-service": "~7.16.0", + "@wdio/cli": "~7.5.2", + "@wdio/concise-reporter": "~7.5.2", + "@wdio/local-runner": "~7.5.2", + "@wdio/mocha-framework": "~7.5.2", + "@wdio/spec-reporter": "~7.19.0", + "@wdio/sync": "~7.5.2", "ajv": "6.12.3", "assert": "^2.0.0", "babel-loader": "^8.0.5", @@ -115,11 +115,12 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -137,28 +138,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.0.tgz", - "integrity": "sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.0.tgz", - "integrity": "sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", + "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", - "@babel/helper-compilation-targets": "^7.19.0", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.0", + "@babel/generator": "^7.19.6", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helpers": "^7.19.4", + "@babel/parser": "^7.19.6", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -174,12 +175,12 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.17.0.tgz", - "integrity": "sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", + "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", "dev": true, "dependencies": { - "eslint-scope": "^5.1.1", + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", "semver": "^6.3.0" }, @@ -192,11 +193,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", - "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.1.tgz", + "integrity": "sha512-u1dMdBUmA7Z0rBB97xh8pIhviK7oItYOkjbsCxTWMknyvbQRBwX7/gn4JXurRdirWMFh+ZtYARqkA6ydogVZpg==", "dependencies": { - "@babel/types": "^7.19.0", + "@babel/types": "^7.20.0", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -204,37 +205,50 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", - "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz", - "integrity": "sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", "dependencies": { - "@babel/compat-data": "^7.19.0", + "@babel/compat-data": "^7.20.0", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" }, "engines": { @@ -245,17 +259,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.17.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", - "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -265,12 +279,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", - "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^5.0.1" + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" }, "engines": { "node": ">=6.9.0" @@ -280,14 +294,12 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", - "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dependencies": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2", @@ -306,11 +318,11 @@ } }, "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", - "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -340,11 +352,11 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", - "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -362,87 +374,91 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", + "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.19.4", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", - "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", + "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.19.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -460,17 +476,17 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "engines": { "node": ">=6.9.0" } @@ -484,27 +500,27 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", - "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", "dependencies": { - "@babel/helper-function-name": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", - "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", "dependencies": { "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -524,9 +540,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz", - "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.1.tgz", + "integrity": "sha512-hp0AYxaZJhxULfM1zyp7Wgr+pSUKBcP3M+PHnSzWGdXOzg/kHWIgiUWARvubhUKGOEw3xqY4x+lyZ9ytBVcELw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -535,11 +551,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", - "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -549,13 +565,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", - "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -565,12 +581,13 @@ } }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", - "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -581,12 +598,12 @@ } }, "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -596,12 +613,12 @@ } }, "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.17.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", - "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.17.6", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -612,11 +629,11 @@ } }, "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", - "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -627,11 +644,11 @@ } }, "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", - "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -642,11 +659,11 @@ } }, "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", - "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -657,11 +674,11 @@ } }, "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", - "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -672,11 +689,11 @@ } }, "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", - "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -687,11 +704,11 @@ } }, "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", - "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -702,15 +719,15 @@ } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", - "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", + "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", "dependencies": { - "@babel/compat-data": "^7.17.0", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" + "@babel/plugin-transform-parameters": "^7.18.8" }, "engines": { "node": ">=6.9.0" @@ -720,11 +737,11 @@ } }, "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", - "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -735,12 +752,12 @@ } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", - "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -751,12 +768,12 @@ } }, "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", - "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.10", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -766,13 +783,13 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", - "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -783,12 +800,12 @@ } }, "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", - "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=4" @@ -855,6 +872,20 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", @@ -961,11 +992,11 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", - "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -975,13 +1006,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", - "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -991,11 +1022,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", - "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1005,11 +1036,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", - "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.0.tgz", + "integrity": "sha512-sXOohbpHZSk7GjxK9b3dKB7CfqUD5DwOH+DggKzOQ7TXYP+RCSbRykfjQmn/zq+rBjycVRtLf9pYhAaEJA786w==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1019,17 +1050,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", - "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" }, "engines": { @@ -1040,11 +1072,11 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", - "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1054,11 +1086,11 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz", - "integrity": "sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.0.tgz", + "integrity": "sha512-1dIhvZfkDVx/zn2S1aFwlruspTt4189j7fEkH0Y0VyuDM6bQt7bD6kLcz3l4IlLG+e5OReaBz9ROAbttRtUHqA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1068,12 +1100,12 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", - "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1083,11 +1115,11 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", - "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1097,12 +1129,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", - "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1112,11 +1144,11 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", - "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1126,13 +1158,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", - "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", "dependencies": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1142,11 +1174,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", - "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1156,11 +1188,11 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", - "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1170,13 +1202,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", - "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1186,14 +1217,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", - "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" }, "engines": { "node": ">=6.9.0" @@ -1203,15 +1233,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "dependencies": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" }, "engines": { "node": ">=6.9.0" @@ -1221,12 +1250,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", - "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1236,11 +1265,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", - "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1250,11 +1280,11 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", - "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1264,12 +1294,12 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", - "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1279,11 +1309,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", - "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.1.tgz", + "integrity": "sha512-nDvKLrAvl+kf6BOy1UJ3MGwzzfTMgppxwiD2Jb4LO3xjYyZq30oQzDNJbCQpMdG9+j2IXHoiMrw5Cm/L6ZoxXQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1293,11 +1323,11 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", - "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1307,11 +1337,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", - "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", "dependencies": { - "regenerator-transform": "^0.14.2" + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" }, "engines": { "node": ">=6.9.0" @@ -1321,11 +1352,11 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", - "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1335,15 +1366,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz", - "integrity": "sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", "dependencies": { "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "babel-plugin-polyfill-corejs2": "^0.3.1", - "babel-plugin-polyfill-corejs3": "^0.5.2", - "babel-plugin-polyfill-regenerator": "^0.3.1", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", "semver": "^6.3.0" }, "engines": { @@ -1354,11 +1385,11 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", - "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1368,12 +1399,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", - "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1383,11 +1414,11 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", - "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1397,11 +1428,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", - "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1411,11 +1442,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", - "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1425,11 +1456,11 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", - "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1439,12 +1470,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", - "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1454,36 +1485,37 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", - "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.4.tgz", + "integrity": "sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==", "dependencies": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.11", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.19.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.19.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -1493,44 +1525,44 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.19.4", + "@babel/plugin-transform-classes": "^7.19.0", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.19.4", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.0", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", + "@babel/types": "^7.19.4", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" }, "engines": { @@ -1556,20 +1588,28 @@ } }, "node_modules/@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/runtime/node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "node_modules/@babel/runtime-corejs3": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz", + "integrity": "sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg==", + "dev": true, + "dependencies": { + "core-js-pure": "^3.25.1", + "regenerator-runtime": "^0.13.10" + }, + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/template": { "version": "7.18.10", @@ -1585,18 +1625,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.0.tgz", - "integrity": "sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", + "@babel/generator": "^7.20.1", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.0", - "@babel/types": "^7.19.0", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1605,12 +1645,12 @@ } }, "node_modules/@babel/types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", - "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1663,9 +1703,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1774,7 +1814,7 @@ "node_modules/@gulp-sourcemaps/map-sources": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", - "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", "dev": true, "dependencies": { "normalize-path": "^2.0.1", @@ -1787,7 +1827,7 @@ "node_modules/@gulp-sourcemaps/map-sources/node_modules/normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, "dependencies": { "remove-trailing-separator": "^1.0.1" @@ -1869,76 +1909,6 @@ "node": ">=8" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1949,19 +1919,19 @@ } }, "node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^16.0.0", + "@types/yargs": "^15.0.0", "chalk": "^4.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.14.2" } }, "node_modules/@jest/types/node_modules/ansi-styles": { @@ -2013,6 +1983,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/types/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2026,22 +2005,21 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "engines": { "node": ">=6.0.0" } @@ -2064,18 +2042,41 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" } }, "node_modules/@polka/url": { @@ -2126,19 +2127,16 @@ } }, "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, - "node_modules/@socket.io/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", @@ -2153,9 +2151,9 @@ } }, "node_modules/@types/aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-P+dkdFu0n08PDIvw+9nT9ByQnd+Udc8DaWPb9HKfaPwCvWvQpC5XaMRx2xLWECm9x1VKNps6vEAlirjA6+uNrQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", "dev": true }, "node_modules/@types/cacheable-request": { @@ -2170,12 +2168,6 @@ "@types/responselike": "*" } }, - "node_modules/@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", - "dev": true - }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -2210,15 +2202,15 @@ "dev": true }, "node_modules/@types/ejs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.0.tgz", - "integrity": "sha512-DCg+Ka+uDQ31lJ/UtEXVlaeV3d6t81gifaVWKJy4MYVVgvJttyX/viREy+If7fz+tK/gVxTGMtyrFPnm4gjrVA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.1.tgz", + "integrity": "sha512-RQul5wEfY7BjWm0sYY86cmUN/pcXWGyVxWX93DFFJvcrxax5zKlieLwA3T77xJGwNcZW0YW6CYG70p1m8xPFmA==", "dev": true }, "node_modules/@types/eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "version": "8.4.9", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.9.tgz", + "integrity": "sha512-jFCSo4wJzlHQLCpceUhUnXdrPuCNOjGFMQ8Eg6JXxlz3QaCKOb7eGi2cephQdM4XTYsNej69P9JDJ1zqNIbncQ==", "dev": true, "dependencies": { "@types/estree": "*", @@ -2226,9 +2218,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -2290,13 +2282,13 @@ "dev": true }, "node_modules/@types/inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-BNoMetRf3gmkpAlV5we+kxyZTle7YibdOntIZbU5pyIfMdcwy784KfeZDAcuyMznkh5OLa17RVXZOGA5LTlkgQ==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", "dev": true, "dependencies": { "@types/through": "*", - "rxjs": "^7.2.0" + "rxjs": "^6.4.0" } }, "node_modules/@types/istanbul-lib-coverage": { @@ -2324,54 +2316,55 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "node_modules/@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-4.2.0.tgz", + "integrity": "sha512-xoBtGl5R9jeKUhc8ZqeYaRDx04qqJ10yhhXYGmJ4Jr8qKpvMsDQQrNUvF/wUJ4klOtmJeJM+p2Xo3zp9uaC3tw==", + "deprecated": "This is a stub types definition. keyv provides its own type definitions, so you do not need this installed.", "dev": true, "dependencies": { - "@types/node": "*" + "keyv": "*" } }, "node_modules/@types/lodash": { - "version": "4.14.179", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz", - "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==", + "version": "4.14.187", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.187.tgz", + "integrity": "sha512-MrO/xLXCaUgZy3y96C/iOsaIqZSeupyTImKClHunL5GrmaiII2VwvWmLBu2hwa0Kp0sV19CsyjtrTc/Fx8rg/A==", "dev": true }, "node_modules/@types/lodash.flattendeep": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.6.tgz", - "integrity": "sha512-uLm2MaRVlqJSGsMK0RZpP5T3KqReq+9WbYDHCUhBhp98v56hMG/Yht52bsoTSui9xz2mUvQ9NfG3LrNGDL92Ng==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.7.tgz", + "integrity": "sha512-1h6GW/AeZw/Wej6uxrqgmdTDZX1yFS39lRsXYkg+3kWvOWWrlGCI6H7lXxlUHOzxDT4QeYGmgPpQ3BX9XevzOg==", "dev": true, "dependencies": { "@types/lodash": "*" } }, "node_modules/@types/lodash.pickby": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz", - "integrity": "sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.pickby/-/lodash.pickby-4.6.7.tgz", + "integrity": "sha512-4ebXRusuLflfscbD0PUX4eVknDHD9Yf+uMtBIvA/hrnTqeAzbuHuDjvnYriLjUrI9YrhCPVKUf4wkRSXJQ6gig==", "dev": true, "dependencies": { "@types/lodash": "*" } }, "node_modules/@types/lodash.union": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.union/-/lodash.union-4.6.6.tgz", - "integrity": "sha512-Wu0ZEVNcyCz8eAn6TlUbYWZoGbH9E+iOHxAZbwUoCEXdUiy6qpcz5o44mMXViM4vlPLLCPlkAubEP1gokoSZaw==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.union/-/lodash.union-4.6.7.tgz", + "integrity": "sha512-6HXM6tsnHJzKgJE0gA/LhTGf/7AbjUk759WZ1MziVm+OBNAATHhdgj+a3KVE8g76GCLAnN4ZEQQG1EGgtBIABA==", "dev": true, "dependencies": { "@types/lodash": "*" @@ -2386,16 +2379,10 @@ "@types/unist": "*" } }, - "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, "node_modules/@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", + "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", "dev": true }, "node_modules/@types/ms": { @@ -2405,9 +2392,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", "dev": true }, "node_modules/@types/normalize-package-data": { @@ -2423,18 +2410,18 @@ "dev": true }, "node_modules/@types/puppeteer": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.5.tgz", - "integrity": "sha512-lxCjpDEY+DZ66+W3x5Af4oHnEmUXt0HuaRzkBGE2UZiZEp/V1d3StpLPlmNVu/ea091bdNmVPl44lu8Wy/0ZCA==", + "version": "5.4.7", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.7.tgz", + "integrity": "sha512-JdGWZZYL0vKapXF4oQTC5hLVNfOgdPrqeZ1BiQnGk5cB7HeE91EWUiTdVSdQPobRN8rIcdffjiOgCYJ/S8QrnQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/recursive-readdir": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.0.tgz", - "integrity": "sha512-HGk753KRu2N4mWduovY4BLjYq4jTOL29gV2OfGdGxHcPSWGFkC5RRIdk+VTs5XmYd7MVAD+JwKrcb5+5Y7FOCg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.1.tgz", + "integrity": "sha512-Xd+Ptc4/F2ueInqy5yK2FI5FxtwwbX2+VZpcg+9oYsFJVen8qQKGapCr+Bi5wQtHU1cTXT8s+07lo/nKPgu8Gg==", "dev": true, "dependencies": { "@types/node": "*" @@ -2514,9 +2501,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", + "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -2529,9 +2516,9 @@ "dev": true }, "node_modules/@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, "dependencies": { @@ -2545,9 +2532,9 @@ "dev": true }, "node_modules/@videojs/http-streaming": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.14.2.tgz", - "integrity": "sha512-K1raSfO/pq5r8iUas3OSYni0kXOj91n8ealIpV02khghzGv9LQ6O3YUqYd/eAhJ1HIrmZWOnrYpK/P+mhUExXQ==", + "version": "2.14.3", + "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.14.3.tgz", + "integrity": "sha512-2tFwxCaNbcEZzQugWf8EERwNMyNtspfHnvxRGRABQs09W/5SqmkWFuGWfUAm4wQKlXGfdPyAJ1338ASl459xAA==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", @@ -2594,14 +2581,14 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.38.tgz", - "integrity": "sha512-/FsvnSu7Z+lkd/8KXMa4yYNUiqQrI22135gfsQYVGuh5tqEgOB0XqrUdb/KnCLa5+TmQLPwvyUnKMyCpu+SX3Q==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.41.tgz", + "integrity": "sha512-oA4mH6SA78DT+96/nsi4p9DX97PHcNROxs51lYk7gb9Z4BPKQ3Mh+BLn6CQZBw857Iuhu28BfMSRHAlPvD4vlw==", "dev": true, "optional": true, "dependencies": { "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.38", + "@vue/shared": "3.2.41", "estree-walker": "^2.0.2", "source-map": "^0.6.1" } @@ -2617,29 +2604,29 @@ } }, "node_modules/@vue/compiler-dom": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.38.tgz", - "integrity": "sha512-zqX4FgUbw56kzHlgYuEEJR8mefFiiyR3u96498+zWPsLeh1WKvgIReoNE+U7gG8bCUdvsrJ0JRmev0Ky6n2O0g==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.41.tgz", + "integrity": "sha512-xe5TbbIsonjENxJsYRbDJvthzqxLNk+tb3d/c47zgREDa/PCp6/Y4gC/skM4H6PIuX5DAxm7fFJdbjjUH2QTMw==", "dev": true, "optional": true, "dependencies": { - "@vue/compiler-core": "3.2.38", - "@vue/shared": "3.2.38" + "@vue/compiler-core": "3.2.41", + "@vue/shared": "3.2.41" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.38.tgz", - "integrity": "sha512-KZjrW32KloMYtTcHAFuw3CqsyWc5X6seb8KbkANSWt3Cz9p2qA8c1GJpSkksFP9ABb6an0FLCFl46ZFXx3kKpg==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.41.tgz", + "integrity": "sha512-+1P2m5kxOeaxVmJNXnBskAn3BenbTmbxBxWOtBq3mQTCokIreuMULFantBUclP0+KnzNCMOvcnKinqQZmiOF8w==", "dev": true, "optional": true, "dependencies": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.38", - "@vue/compiler-dom": "3.2.38", - "@vue/compiler-ssr": "3.2.38", - "@vue/reactivity-transform": "3.2.38", - "@vue/shared": "3.2.38", + "@vue/compiler-core": "3.2.41", + "@vue/compiler-dom": "3.2.41", + "@vue/compiler-ssr": "3.2.41", + "@vue/reactivity-transform": "3.2.41", + "@vue/shared": "3.2.41", "estree-walker": "^2.0.2", "magic-string": "^0.25.7", "postcss": "^8.1.10", @@ -2657,34 +2644,34 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.38.tgz", - "integrity": "sha512-bm9jOeyv1H3UskNm4S6IfueKjUNFmi2kRweFIGnqaGkkRePjwEcfCVqyS3roe7HvF4ugsEkhf4+kIvDhip6XzQ==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.41.tgz", + "integrity": "sha512-Y5wPiNIiaMz/sps8+DmhaKfDm1xgj6GrH99z4gq2LQenfVQcYXmHIOBcs5qPwl7jaW3SUQWjkAPKMfQemEQZwQ==", "dev": true, "optional": true, "dependencies": { - "@vue/compiler-dom": "3.2.38", - "@vue/shared": "3.2.38" + "@vue/compiler-dom": "3.2.41", + "@vue/shared": "3.2.41" } }, "node_modules/@vue/reactivity-transform": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.38.tgz", - "integrity": "sha512-3SD3Jmi1yXrDwiNJqQ6fs1x61WsDLqVk4NyKVz78mkaIRh6d3IqtRnptgRfXn+Fzf+m6B1KxBYWq1APj6h4qeA==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.41.tgz", + "integrity": "sha512-mK5+BNMsL4hHi+IR3Ft/ho6Za+L3FA5j8WvreJ7XzHrqkPq8jtF/SMo7tuc9gHjLDwKZX1nP1JQOKo9IEAn54A==", "dev": true, "optional": true, "dependencies": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.38", - "@vue/shared": "3.2.38", + "@vue/compiler-core": "3.2.41", + "@vue/shared": "3.2.41", "estree-walker": "^2.0.2", "magic-string": "^0.25.7" } }, "node_modules/@vue/shared": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.38.tgz", - "integrity": "sha512-dTyhTIRmGXBjxJE+skC8tTWCGLCVc4wQgRRLt8+O9p5ewBAjoBwtCAkLPrtToSr1xltoe3st21Pv953aOZ7alg==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.41.tgz", + "integrity": "sha512-W9mfWLHmJhkfAmV+7gDjcHeAWALQtgGT3JErxULl0oz6R6+3ug91I7IErs93eCFhPCZPHBs4QJS7YWEV7A3sxw==", "dev": true, "optional": true }, @@ -2708,37 +2695,224 @@ "@wdio/cli": "^7.0.0" } }, - "node_modules/@wdio/cli": { + "node_modules/@wdio/browserstack-service/node_modules/@wdio/config": { "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.16.16.tgz", - "integrity": "sha512-Wz/e5zm1UNHB9RAIsJIM7ioDzVllUwTvhVWOrI7HR/53GmO/cIvAVjpnlglizJNgK8WlbnM/cKNVIXxqxrnFmw==", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.16.16.tgz", + "integrity": "sha512-K/ObPuo6Da2liz++OKOIfbdpFwI7UWiFcBylfJkCYbweuXCoW1aUqlKI6rmKPwCH9Uqr/RHWu6p8eo0zWe6xVA==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.16.0", + "@wdio/types": "7.16.14", + "deepmerge": "^4.0.0", + "glob": "^7.1.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/@wdio/protocols": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.16.7.tgz", + "integrity": "sha512-Wv40pNQcLiPzQ3o98Mv4A8T1EBQ6k4khglz/e2r16CTm+F3DDYh8eLMAsU5cgnmuwwDKX1EyOiFwieykBn5MCg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/@wdio/repl": { + "version": "7.16.14", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.16.14.tgz", + "integrity": "sha512-Ezih0Y+lsGkKv3H3U56hdWgZiQGA3VaAYguSLd9+g1xbQq+zMKqSmfqECD9bAy+OgCCiVTRstES6lHZxJVPhAg==", + "dev": true, + "dependencies": { + "@wdio/utils": "7.16.14" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/@wdio/utils": { + "version": "7.16.14", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.16.14.tgz", + "integrity": "sha512-wwin8nVpIlhmXJkq6GJw9aDDzgLOJKgXTcEua0T2sdXjoW78u5Ly/GZrFXTjMGhacFvoZfitTrjyfyy4CxMVvw==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.16.0", + "@wdio/types": "7.16.14", + "p-iteration": "^1.1.8" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/devtools": { + "version": "7.16.16", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.16.16.tgz", + "integrity": "sha512-M0kzkuSgfEhpqIis3gdtWsNjn/HQ+vRAmEzDnbYx/7FfjFxhSv1d+rOOT20pvd60soItMYpsOova1igACEGkGQ==", + "dev": true, + "dependencies": { + "@types/node": "^17.0.4", + "@types/ua-parser-js": "^0.7.33", + "@wdio/config": "7.16.16", + "@wdio/logger": "7.16.0", + "@wdio/protocols": "7.16.7", + "@wdio/types": "7.16.14", + "@wdio/utils": "7.16.14", + "chrome-launcher": "^0.15.0", + "edge-paths": "^2.1.0", + "puppeteer-core": "^13.1.3", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/devtools-protocol": { + "version": "0.0.973690", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.973690.tgz", + "integrity": "sha512-myh3hSFp0YWa2GED11PmbLhV4dv9RdO7YUz27XJrbQLnP5bMbZL6dfOOILTHO57yH0kX5GfuOZBsg/4NamfPvQ==", + "dev": true + }, + "node_modules/@wdio/browserstack-service/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/ua-parser-js": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.32.tgz", + "integrity": "sha512-dXVsz3M4j+5tTiovFVyVqssXBu5HM47//YSOeZ9fQkdDKkfzv2v3PP1jmH6FUyPW+yCSn7aBVK1fGGKNhowdDA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/webdriver": { + "version": "7.16.16", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.16.16.tgz", + "integrity": "sha512-x8UoG9k/P8KDrfSh1pOyNevt9tns3zexoMxp9cKnyA/7HYSErhZYTLGlgxscAXLtQG41cMH/Ba/oBmOx7Hgd8w==", + "dev": true, + "dependencies": { + "@types/node": "^17.0.4", + "@wdio/config": "7.16.16", + "@wdio/logger": "7.16.0", + "@wdio/protocols": "7.16.7", + "@wdio/types": "7.16.14", + "@wdio/utils": "7.16.14", + "got": "^11.0.2", + "ky": "^0.29.0", + "lodash.merge": "^4.6.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/webdriverio": { + "version": "7.16.16", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.16.16.tgz", + "integrity": "sha512-caPaEWyuD3Qoa7YkW4xCCQA4v9Pa9wmhFGPvNZh3ERtjMCNi8L/XXOdkekWNZmFh3tY0kFguBj7+fAwSY7HAGw==", + "dev": true, + "dependencies": { + "@types/aria-query": "^5.0.0", + "@types/node": "^17.0.4", + "@wdio/config": "7.16.16", + "@wdio/logger": "7.16.0", + "@wdio/protocols": "7.16.7", + "@wdio/repl": "7.16.14", + "@wdio/types": "7.16.14", + "@wdio/utils": "7.16.14", + "archiver": "^5.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools": "7.16.16", + "devtools-protocol": "^0.0.973690", + "fs-extra": "^10.0.0", + "get-port": "^5.1.1", + "grapheme-splitter": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "lodash.isobject": "^3.0.2", + "lodash.isplainobject": "^4.0.6", + "lodash.zip": "^4.2.0", + "minimatch": "^5.0.0", + "puppeteer-core": "^13.1.3", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^8.0.0", + "webdriver": "7.16.16" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/cli": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.5.7.tgz", + "integrity": "sha512-nOQJLskrY+UECDd3NxE7oBzb6cDA7e7x02YWQugOlOgnZ4a+PJmkFoSsO8C2uNCpdFngy5rJKGUo5vbtAHEF9Q==", "dev": true, "dependencies": { "@types/ejs": "^3.0.5", "@types/fs-extra": "^9.0.4", - "@types/inquirer": "^8.1.2", + "@types/inquirer": "^7.3.1", "@types/lodash.flattendeep": "^4.4.6", "@types/lodash.pickby": "^4.6.6", "@types/lodash.union": "^4.6.6", - "@types/node": "^17.0.4", "@types/recursive-readdir": "^2.2.0", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", "async-exit-hook": "^2.0.1", "chalk": "^4.0.0", "chokidar": "^3.0.0", "cli-spinners": "^2.1.0", "ejs": "^3.0.1", "fs-extra": "^10.0.0", - "inquirer": "8.1.5", + "inquirer": "^8.0.0", "lodash.flattendeep": "^4.4.0", "lodash.pickby": "^4.6.0", "lodash.union": "^4.6.0", "mkdirp": "^1.0.4", "recursive-readdir": "^2.2.2", - "webdriverio": "7.16.16", + "webdriverio": "7.5.7", "yargs": "^17.0.0", "yarn-install": "^1.0.0" }, @@ -2749,6 +2923,39 @@ "node": ">=12.0.0" } }, + "node_modules/@wdio/cli/node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "node_modules/@wdio/cli/node_modules/@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/cli/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/@wdio/cli/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2764,6 +2971,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/@wdio/cli/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, "node_modules/@wdio/cli/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2780,6 +3000,32 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@wdio/cli/node_modules/chrome-launcher": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", + "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^1.0.5", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^0.5.3", + "rimraf": "^3.0.2" + } + }, + "node_modules/@wdio/cli/node_modules/chrome-launcher/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/@wdio/cli/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2798,6 +3044,72 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/@wdio/cli/node_modules/devtools": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", + "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "dev": true, + "dependencies": { + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "chrome-launcher": "^0.13.1", + "edge-paths": "^2.1.0", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^0.7.21", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/cli/node_modules/devtools-protocol": { + "version": "0.0.878340", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", + "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", + "dev": true + }, + "node_modules/@wdio/cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/cli/node_modules/puppeteer-core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", + "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.869402", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "engines": { + "node": ">=10.18.1" + } + }, + "node_modules/@wdio/cli/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.869402", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", + "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", + "dev": true + }, "node_modules/@wdio/cli/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2810,13 +3122,82 @@ "node": ">=8" } }, + "node_modules/@wdio/cli/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@wdio/cli/node_modules/webdriverio": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", + "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", + "dev": true, + "dependencies": { + "@types/aria-query": "^4.2.1", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/repl": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "archiver": "^5.0.0", + "aria-query": "^4.2.2", + "atob": "^2.1.2", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools": "7.5.7", + "devtools-protocol": "^0.0.878340", + "fs-extra": "^10.0.0", + "get-port": "^5.1.1", + "grapheme-splitter": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "lodash.isobject": "^3.0.2", + "lodash.isplainobject": "^4.0.6", + "lodash.zip": "^4.2.0", + "minimatch": "^3.0.4", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^8.0.0", + "webdriver": "7.5.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/cli/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@wdio/cli/node_modules/yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", + "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", "dev": true, "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", @@ -2829,13 +3210,13 @@ } }, "node_modules/@wdio/concise-reporter": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.16.14.tgz", - "integrity": "sha512-CR+9+skJ3mXPIdRo0AnIJTJHOArrWdKlXTnyZ/DD6M9VrNk5aiTWQyphT/IeHV5+fxjHlMNIf/KgIhj1ewschQ==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.5.7.tgz", + "integrity": "sha512-964i7eQ4sboSla2bdR8714Er82QBgS6u39GmDFX8Izy9Ge38xaE75HuF5S7mnOWGzSojCWgqtwy5k7Rfg6GE3g==", "dev": true, "dependencies": { - "@wdio/reporter": "7.16.14", - "@wdio/types": "7.16.14", + "@wdio/reporter": "7.5.7", + "@wdio/types": "7.5.3", "chalk": "^4.0.0", "pretty-ms": "^7.0.0" }, @@ -2846,6 +3227,18 @@ "@wdio/cli": "^7.0.0" } }, + "node_modules/@wdio/concise-reporter/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/@wdio/concise-reporter/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2895,6 +3288,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/@wdio/concise-reporter/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@wdio/concise-reporter/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2908,13 +3310,13 @@ } }, "node_modules/@wdio/config": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.16.16.tgz", - "integrity": "sha512-K/ObPuo6Da2liz++OKOIfbdpFwI7UWiFcBylfJkCYbweuXCoW1aUqlKI6rmKPwCH9Uqr/RHWu6p8eo0zWe6xVA==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.5.3.tgz", + "integrity": "sha512-udvVizYoilOxuWj/BmoN6y7ZCd4wPdYNlSfWznrbCezAdaLZ4/pNDOO0WRWx2C4+q1wdkXZV/VuQPUGfL0lEHQ==", "dev": true, "dependencies": { - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", "deepmerge": "^4.0.0", "glob": "^7.1.2" }, @@ -2922,19 +3324,116 @@ "node": ">=12.0.0" } }, + "node_modules/@wdio/config/node_modules/@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/config/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wdio/config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wdio/config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@wdio/config/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/config/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@wdio/local-runner": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.16.16.tgz", - "integrity": "sha512-AJaOyM842PWgMffrrXyHJjouVseLHoiL5U1sw2VVproi3ORWHbltl1AMnreU/lrGu9L0CVKHYT1pxu5UbSOCxQ==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.5.7.tgz", + "integrity": "sha512-aYc0XUV+/e3cg8Fp+CWlC4FbwSSG3mKAv1iuy/+Hwzg2kJE+aa+Rf2p2BQYc7HPRtKNW0bM8o+aCImZLAiPM+A==", "dev": true, "dependencies": { "@types/stream-buffers": "^3.0.3", - "@wdio/logger": "7.16.0", - "@wdio/repl": "7.16.14", - "@wdio/runner": "7.16.16", - "@wdio/types": "7.16.14", + "@wdio/logger": "7.5.3", + "@wdio/repl": "7.5.3", + "@wdio/runner": "7.5.7", + "@wdio/types": "7.5.3", "async-exit-hook": "^2.0.1", - "split2": "^4.0.0", + "split2": "^3.2.2", "stream-buffers": "^3.0.2" }, "engines": { @@ -2944,6 +3443,103 @@ "@wdio/cli": "^7.0.0" } }, + "node_modules/@wdio/local-runner/node_modules/@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/local-runner/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/local-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wdio/local-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/local-runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wdio/local-runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@wdio/local-runner/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/local-runner/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@wdio/logger": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.16.0.tgz", @@ -3008,6 +3604,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/@wdio/logger/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@wdio/logger/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3021,22 +3626,58 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "7.16.15", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.16.15.tgz", - "integrity": "sha512-XRya85/RYPZk4MZ7Cvl3oudTdrOo+RyO8b5Ff+dH8hD3GBCACaWgW9AjbsyhvbSTdUlF0gNLPdqOCsxV5XyM3w==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.5.3.tgz", + "integrity": "sha512-96QCVWsiyZxEgOZP3oTq2B2T7zne5dCdehLa2n4q/BLjk96Rj0jifidJZfd/1+vdNPKX0gWWAzpy98Znn8MVMw==", "dev": true, "dependencies": { - "@types/mocha": "^9.0.0", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", - "expect-webdriverio": "^3.0.0", - "mocha": "^9.0.0" + "@types/mocha": "^8.0.0", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "expect-webdriverio": "^2.0.0", + "mocha": "^8.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/mocha-framework/node_modules/@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/mocha-framework/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" }, "engines": { "node": ">=12.0.0" } }, + "node_modules/@wdio/mocha-framework/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@wdio/mocha-framework/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3074,16 +3715,36 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@wdio/mocha-framework/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/mocha-framework/node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" }, "engines": { - "node": ">=8" + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" + } + }, + "node_modules/@wdio/mocha-framework/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, "node_modules/@wdio/mocha-framework/node_modules/color-convert": { @@ -3104,6 +3765,32 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/@wdio/mocha-framework/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@wdio/mocha-framework/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/@wdio/mocha-framework/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3132,10 +3819,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@wdio/mocha-framework/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@wdio/mocha-framework/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@wdio/mocha-framework/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "dependencies": { "argparse": "^2.0.1" @@ -3160,19 +3876,15 @@ } }, "node_modules/@wdio/mocha-framework/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "chalk": "^4.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@wdio/mocha-framework/node_modules/minimatch": { @@ -3188,32 +3900,33 @@ } }, "node_modules/@wdio/mocha-framework/node_modules/mocha": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.1.tgz", - "integrity": "sha512-T7uscqjJVS46Pq1XDXyo9Uvey9gd3huT/DD9cYBb4K2Xc/vbKRPUWK067bxDQRK0yIz6Jxk73IrnimvASzBNAQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", + "chokidar": "3.5.1", + "debug": "4.3.1", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.2.0", + "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.2.0", - "serialize-javascript": "6.0.0", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.2.0", + "wide-align": "1.1.3", + "workerpool": "6.1.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -3223,23 +3936,38 @@ "mocha": "bin/mocha" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 10.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mochajs" } }, - "node_modules/@wdio/mocha-framework/node_modules/ms": { + "node_modules/@wdio/mocha-framework/node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/@wdio/mocha-framework/node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/@wdio/mocha-framework/node_modules/nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -3278,13 +4006,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/mocha-framework/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/@wdio/mocha-framework/node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">=8" + "node": ">=8.10.0" + } + }, + "node_modules/@wdio/mocha-framework/node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" } }, "node_modules/@wdio/mocha-framework/node_modules/strip-json-comments": { @@ -3299,6 +4039,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@wdio/mocha-framework/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/mocha-framework/node_modules/workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, "node_modules/@wdio/mocha-framework/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -3327,75 +4085,347 @@ } }, "node_modules/@wdio/protocols": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.16.7.tgz", - "integrity": "sha512-Wv40pNQcLiPzQ3o98Mv4A8T1EBQ6k4khglz/e2r16CTm+F3DDYh8eLMAsU5cgnmuwwDKX1EyOiFwieykBn5MCg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.5.3.tgz", + "integrity": "sha512-lpNaKwxYhDSL6neDtQQYXvzMAw+u4PXx65ryeMEX82mkARgzSZps5Kyrg9ub7X4T17K1NPfnY6UhZEWg6cKJCg==", "dev": true, "engines": { "node": ">=12.0.0" } }, "node_modules/@wdio/repl": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.16.14.tgz", - "integrity": "sha512-Ezih0Y+lsGkKv3H3U56hdWgZiQGA3VaAYguSLd9+g1xbQq+zMKqSmfqECD9bAy+OgCCiVTRstES6lHZxJVPhAg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.5.3.tgz", + "integrity": "sha512-jfNJwNoc2nWdnLsFoGHmOJR9zaWfDTBMWM3W1eR5kXIjevD6gAfWsB5ZoA4IdybujCXxdnhlsm4o2jIzp/6f7A==", "dev": true, "dependencies": { - "@wdio/utils": "7.16.14" + "@wdio/utils": "7.5.3" }, "engines": { "node": ">=12.0.0" } }, "node_modules/@wdio/reporter": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.16.14.tgz", - "integrity": "sha512-e/I2oGfqjx9+zI4NT/garqxm7Afnos1EcrGSNu75WmP3PNJt4i+9DKkROu4PM6XWcpUB4v2UF7Mv/NrL3TU9aA==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.5.7.tgz", + "integrity": "sha512-9PXqZtCXDtU6UYLNDPu9MZQ8BiABGnRlJTrlbYB3gBfZDibMkJMvwXzPderipBv2+ifDZXmGe3Njf1ao2TkbFA==", "dev": true, "dependencies": { - "@types/diff": "^5.0.0", - "@types/node": "^17.0.4", - "@types/object-inspect": "^1.8.0", - "@types/supports-color": "^8.1.0", - "@types/tmp": "^0.2.0", - "@wdio/types": "7.16.14", - "diff": "^5.0.0", - "fs-extra": "^10.0.0", - "object-inspect": "^1.10.3", - "supports-color": "8.1.1" + "@wdio/types": "7.5.3", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/reporter/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" }, "engines": { "node": ">=12.0.0" } }, "node_modules/@wdio/runner": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.16.16.tgz", - "integrity": "sha512-Tt2ja6GukGPq1m98WP26yOWUGwzK1y7gPTLy6rKlamz3mOBC7koL0T9+iqcFREquUe4CMy2jWp1lqvPlwMbu7g==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.5.7.tgz", + "integrity": "sha512-RzVXd+xnwK/thkx1/xo9K5iscQ0Ofobgsx5dNVtwLDVMn9V7jCW/WX4dSCPAPaVSqnUCmkcQp3P5AoSBPpCZnQ==", "dev": true, "dependencies": { - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", "deepmerge": "^4.0.0", "gaze": "^1.1.2", - "webdriver": "7.16.16", - "webdriverio": "7.16.16" + "webdriver": "7.5.3", + "webdriverio": "7.5.7" }, "engines": { "node": ">=12.0.0" } }, - "node_modules/@wdio/spec-reporter": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.19.1.tgz", - "integrity": "sha512-qnZkn3VcyBPtcorUtpyCFE8v5ubyWmR7mFETXNzyriHyvjvk+NeFCWaFcIehpXYXiAmNpAwyfnZoIY6tkKQixQ==", + "node_modules/@wdio/runner/node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "node_modules/@wdio/runner/node_modules/@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", "dev": true, "dependencies": { - "@types/easy-table": "^0.0.33", - "@wdio/reporter": "7.19.1", - "@wdio/types": "7.19.1", + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/runner/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wdio/runner/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@wdio/runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/runner/node_modules/chrome-launcher": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", + "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^1.0.5", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^0.5.3", + "rimraf": "^3.0.2" + } + }, + "node_modules/@wdio/runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wdio/runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@wdio/runner/node_modules/devtools": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", + "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "dev": true, + "dependencies": { + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "chrome-launcher": "^0.13.1", + "edge-paths": "^2.1.0", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^0.7.21", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/runner/node_modules/devtools-protocol": { + "version": "0.0.878340", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", + "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", + "dev": true + }, + "node_modules/@wdio/runner/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/runner/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/@wdio/runner/node_modules/puppeteer-core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", + "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.869402", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "engines": { + "node": ">=10.18.1" + } + }, + "node_modules/@wdio/runner/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.869402", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", + "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", + "dev": true + }, + "node_modules/@wdio/runner/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/runner/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@wdio/runner/node_modules/webdriverio": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", + "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", + "dev": true, + "dependencies": { + "@types/aria-query": "^4.2.1", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/repl": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "archiver": "^5.0.0", + "aria-query": "^4.2.2", + "atob": "^2.1.2", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools": "7.5.7", + "devtools-protocol": "^0.0.878340", + "fs-extra": "^10.0.0", + "get-port": "^5.1.1", + "grapheme-splitter": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "lodash.isobject": "^3.0.2", + "lodash.isplainobject": "^4.0.6", + "lodash.zip": "^4.2.0", + "minimatch": "^3.0.4", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^8.0.0", + "webdriver": "7.5.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/runner/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@wdio/spec-reporter": { + "version": "7.19.7", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.19.7.tgz", + "integrity": "sha512-BDBZU2EK/GuC9VxtfqPtoW43FmvKxYDsvcDVDi3F7o+9fkcuGSJiWbw1AX251ZzzVQ7YP9ImTitSpdpUKXkilQ==", + "dev": true, + "dependencies": { + "@types/easy-table": "^0.0.33", + "@wdio/reporter": "7.19.7", + "@wdio/types": "7.19.5", "chalk": "^4.0.0", "easy-table": "^1.1.1", "pretty-ms": "^7.0.0" @@ -3408,9 +4438,9 @@ } }, "node_modules/@wdio/spec-reporter/node_modules/@wdio/reporter": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.19.1.tgz", - "integrity": "sha512-sWmBBV4dPCZkGk9Qq0m35T/vHGen0N10nH4osQcVP3IZJqpo2eLIH4w+X6EUbjZ2GdgOA2bLMMzb1bl9JqnGPg==", + "version": "7.19.7", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.19.7.tgz", + "integrity": "sha512-Dum19gpfru66FnIq78/4HTuW87B7ceLDp6PJXwQM5kXyN7Gb7zhMgp6FZTM0FCYLyi6U/zXZSvpNUYl77caS6g==", "dev": true, "dependencies": { "@types/diff": "^5.0.0", @@ -3418,7 +4448,7 @@ "@types/object-inspect": "^1.8.0", "@types/supports-color": "^8.1.0", "@types/tmp": "^0.2.0", - "@wdio/types": "7.19.1", + "@wdio/types": "7.19.5", "diff": "^5.0.0", "fs-extra": "^10.0.0", "object-inspect": "^1.10.3", @@ -3428,7 +4458,93 @@ "node": ">=12.0.0" } }, - "node_modules/@wdio/spec-reporter/node_modules/@wdio/reporter/node_modules/supports-color": { + "node_modules/@wdio/spec-reporter/node_modules/@wdio/types": { + "version": "7.19.5", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.19.5.tgz", + "integrity": "sha512-S1lC0pmtEO7NVH/2nM1c7NHbkgxLZH3VVG/z6ym3Bbxdtcqi2LMsEvvawMAU/fmhyiIkMsGZCO8vxG9cRw4z4A==", + "dev": true, + "dependencies": { + "@types/node": "^17.0.4", + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "^4.6.2" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@wdio/spec-reporter/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", @@ -3443,23 +4559,57 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/@wdio/spec-reporter/node_modules/@wdio/types": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.19.1.tgz", - "integrity": "sha512-mOodKlmvYxpj8P5BhjggEGpXuiRSlsyn2ClG8QqJ3lfXgOtOVEzFNfv/Ai7TkHr+lHDQNXLjllCjSqoCHhwlqg==", + "node_modules/@wdio/sync": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.5.7.tgz", + "integrity": "sha512-Zu/AYLjwqbFSbaOU1US7ownv3ov8JrtoGHq51JfJ4masefJDXNkHix2cZ0qEgl3IvkkWQ0ewL0G8GTXb3KOemA==", "dev": true, "dependencies": { - "@types/node": "^17.0.4", - "got": "^11.8.1" + "@types/fibers": "^3.1.0", + "@types/puppeteer": "^5.4.0", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", + "fibers": "^5.0.0", + "webdriverio": "7.5.7" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/sync/node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "node_modules/@wdio/sync/node_modules/@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=12.0.0" + } + }, + "node_modules/@wdio/sync/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" }, - "peerDependencies": { - "typescript": "^4.6.2" + "engines": { + "node": ">=12.0.0" } }, - "node_modules/@wdio/spec-reporter/node_modules/ansi-styles": { + "node_modules/@wdio/sync/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -3474,7 +4624,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@wdio/spec-reporter/node_modules/chalk": { + "node_modules/@wdio/sync/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@wdio/sync/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -3490,7 +4653,21 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@wdio/spec-reporter/node_modules/color-convert": { + "node_modules/@wdio/sync/node_modules/chrome-launcher": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", + "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^1.0.5", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^0.5.3", + "rimraf": "^3.0.2" + } + }, + "node_modules/@wdio/sync/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -3502,13 +4679,91 @@ "node": ">=7.0.0" } }, - "node_modules/@wdio/spec-reporter/node_modules/color-name": { + "node_modules/@wdio/sync/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/@wdio/spec-reporter/node_modules/supports-color": { + "node_modules/@wdio/sync/node_modules/devtools": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", + "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "dev": true, + "dependencies": { + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "chrome-launcher": "^0.13.1", + "edge-paths": "^2.1.0", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^0.7.21", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/sync/node_modules/devtools-protocol": { + "version": "0.0.878340", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", + "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", + "dev": true + }, + "node_modules/@wdio/sync/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/sync/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/@wdio/sync/node_modules/puppeteer-core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", + "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.869402", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "engines": { + "node": ">=10.18.1" + } + }, + "node_modules/@wdio/sync/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.869402", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", + "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", + "dev": true + }, + "node_modules/@wdio/sync/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -3520,21 +4775,73 @@ "node": ">=8" } }, - "node_modules/@wdio/sync": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.16.16.tgz", - "integrity": "sha512-MbVFAteaAOxHLKkMiMzOnh1hzINAK2U41GDIfy1yaPumcw1pNuJIhWrBYxprNMlqt8srk++wqQWgj5XpFjCL6g==", + "node_modules/@wdio/sync/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@wdio/sync/node_modules/webdriverio": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", + "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", "dev": true, "dependencies": { - "@types/fibers": "^3.1.0", - "@types/puppeteer": "^5.4.0", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "fibers": "^5.0.0", - "webdriverio": "7.16.16" + "@types/aria-query": "^4.2.1", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/repl": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "archiver": "^5.0.0", + "aria-query": "^4.2.2", + "atob": "^2.1.2", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools": "7.5.7", + "devtools-protocol": "^0.0.878340", + "fs-extra": "^10.0.0", + "get-port": "^5.1.1", + "grapheme-splitter": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "lodash.isobject": "^3.0.2", + "lodash.isplainobject": "^4.0.6", + "lodash.zip": "^4.2.0", + "minimatch": "^3.0.4", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^8.0.0", + "webdriver": "7.5.3" }, "engines": { - "node": ">=12.0.0 <16" + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/sync/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/@wdio/types": { @@ -3551,19 +4858,115 @@ } }, "node_modules/@wdio/utils": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.16.14.tgz", - "integrity": "sha512-wwin8nVpIlhmXJkq6GJw9aDDzgLOJKgXTcEua0T2sdXjoW78u5Ly/GZrFXTjMGhacFvoZfitTrjyfyy4CxMVvw==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.5.3.tgz", + "integrity": "sha512-nlLDKr8v8abLOHCKroBwQkGPdCIxjID2MllgWX23xqkYZylM9RdwPBdL8osQt9m3rq2TxiPAT4OlbzNt2WtN6Q==", "dev": true, "dependencies": { - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "p-iteration": "^1.1.8" + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/utils/node_modules/@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=12.0.0" } }, + "node_modules/@wdio/utils/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wdio/utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wdio/utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@wdio/utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -3711,9 +5114,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.6.tgz", - "integrity": "sha512-HHXP9hskkFQHy8QxxUXkS7946FFIhYVfGqsk0WLwllmexN9x/+R4UBLvurHEuyXRfVEObVR8APuQehykLviwSQ==", + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.8.tgz", + "integrity": "sha512-PrJx38EfpitFhwmILRl37jAdBlsww6AZ6rRVK4QS7T7RHLhX7mSs647sTmgr9GIxe3qjXdesmomEgbgaokrVFg==", "dev": true, "engines": { "node": ">=10.0.0" @@ -3734,7 +5137,7 @@ "node_modules/abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", "dev": true }, "node_modules/accepts": { @@ -3770,6 +5173,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/aes-decrypter": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.1.3.tgz", @@ -3783,10 +5195,13 @@ } }, "node_modules/agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, + "dependencies": { + "debug": "4" + }, "engines": { "node": ">= 6.0.0" } @@ -3807,10 +5222,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", "dev": true, "optional": true, "engines": { @@ -3818,9 +5242,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -3856,7 +5280,7 @@ "node_modules/ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", "dev": true, "dependencies": { "ansi-wrap": "0.1.0" @@ -3900,7 +5324,7 @@ "node_modules/ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -3922,7 +5346,7 @@ "node_modules/append-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", "dev": true, "dependencies": { "buffer-equal": "^1.0.0" @@ -3932,13 +5356,13 @@ } }, "node_modules/archiver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz", - "integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", + "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", "dev": true, "dependencies": { "archiver-utils": "^2.1.0", - "async": "^3.2.0", + "async": "^3.2.3", "buffer-crc32": "^0.2.1", "readable-stream": "^3.6.0", "readdir-glob": "^1.0.0", @@ -3971,9 +5395,9 @@ } }, "node_modules/archiver/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "node_modules/archiver/node_modules/readable-stream": { @@ -3993,7 +5417,7 @@ "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, "node_modules/argparse": { @@ -4006,18 +5430,31 @@ } }, "node_modules/aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, - "engines": { - "node": ">=6.0" + "dependencies": { + "deep-equal": "^2.0.5" } }, "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-diff/node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4026,7 +5463,7 @@ "node_modules/arr-filter": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", "dev": true, "dependencies": { "make-iterator": "^1.0.0" @@ -4047,7 +5484,7 @@ "node_modules/arr-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", "dev": true, "dependencies": { "make-iterator": "^1.0.0" @@ -4057,9 +5494,9 @@ } }, "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4068,7 +5505,7 @@ "node_modules/array-differ": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4077,7 +5514,7 @@ "node_modules/array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4086,23 +5523,23 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/array-from": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==", "dev": true }, "node_modules/array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", "get-intrinsic": "^1.1.1", "is-string": "^1.0.7" }, @@ -4116,7 +5553,7 @@ "node_modules/array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", - "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "integrity": "sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==", "dev": true, "dependencies": { "array-slice": "^1.0.0", @@ -4182,7 +5619,7 @@ "node_modules/array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4191,21 +5628,22 @@ "node_modules/array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4238,7 +5676,7 @@ "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "engines": { "node": ">=0.8" @@ -4256,7 +5694,7 @@ "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4274,7 +5712,7 @@ "node_modules/async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", "dev": true }, "node_modules/async-done": { @@ -4310,7 +5748,7 @@ "node_modules/async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", - "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "integrity": "sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==", "dev": true, "dependencies": { "async-done": "^1.2.2" @@ -4322,7 +5760,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "node_modules/atob": { @@ -4352,7 +5790,7 @@ "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, "engines": { "node": "*" @@ -4367,7 +5805,7 @@ "node_modules/babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", "dev": true, "dependencies": { "chalk": "^1.1.3", @@ -4378,7 +5816,7 @@ "node_modules/babel-code-frame/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4387,7 +5825,7 @@ "node_modules/babel-code-frame/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4396,7 +5834,7 @@ "node_modules/babel-code-frame/node_modules/chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "dependencies": { "ansi-styles": "^2.2.1", @@ -4412,13 +5850,13 @@ "node_modules/babel-code-frame/node_modules/js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", "dev": true }, "node_modules/babel-code-frame/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -4430,7 +5868,7 @@ "node_modules/babel-code-frame/node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "engines": { "node": ">=0.8.0" @@ -4475,7 +5913,7 @@ "node_modules/babel-core/node_modules/json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -4484,18 +5922,9 @@ "node_modules/babel-core/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/babel-core/node_modules/slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", @@ -4512,22 +5941,10 @@ "trim-right": "^1.0.1" } }, - "node_modules/babel-generator/node_modules/detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "dependencies": { - "repeating": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/babel-generator/node_modules/jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==", "dev": true, "bin": { "jsesc": "bin/jsesc" @@ -4536,7 +5953,7 @@ "node_modules/babel-helpers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==", "dev": true, "dependencies": { "babel-runtime": "^6.22.0", @@ -4544,13 +5961,13 @@ } }, "node_modules/babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dev": true, "dependencies": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" }, @@ -4562,49 +5979,15 @@ "webpack": ">=2" } }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/babel-messages": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", "dev": true, "dependencies": { "babel-runtime": "^6.22.0" } }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -4621,29 +6004,13 @@ "node": ">=8" } }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", - "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", - "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dependencies": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.3.1", + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" }, "peerDependencies": { @@ -4651,23 +6018,23 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", - "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", - "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1" + "@babel/helper-define-polyfill-provider": "^0.3.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -4676,7 +6043,7 @@ "node_modules/babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==", "dev": true, "dependencies": { "babel-core": "^6.26.0", @@ -4708,19 +6075,10 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/babel-register/node_modules/source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "dependencies": { - "source-map": "^0.5.6" - } - }, "node_modules/babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", "dev": true, "dependencies": { "core-js": "^2.4.0", @@ -4735,10 +6093,16 @@ "dev": true, "hasInstallScript": true }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, "node_modules/babel-template": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", "dev": true, "dependencies": { "babel-runtime": "^6.26.0", @@ -4751,7 +6115,7 @@ "node_modules/babel-traverse": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", "dev": true, "dependencies": { "babel-code-frame": "^6.26.0", @@ -4786,13 +6150,13 @@ "node_modules/babel-traverse/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "node_modules/babel-types": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", "dev": true, "dependencies": { "babel-runtime": "^6.26.0", @@ -4804,7 +6168,7 @@ "node_modules/babel-types/node_modules/to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4822,7 +6186,7 @@ "node_modules/bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", - "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "integrity": "sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==", "dev": true, "dependencies": { "arr-filter": "^1.1.1", @@ -4876,7 +6240,7 @@ "node_modules/base/node_modules/define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, "dependencies": { "is-descriptor": "^1.0.0" @@ -4926,16 +6290,22 @@ "node": ">= 0.8" } }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "dependencies": { "tweetnacl": "^0.14.3" @@ -4944,7 +6314,7 @@ "node_modules/beeper": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "integrity": "sha512-3vqtKL1N45I5dV0RdssXZG7X6pCqQrWPNOlBPZPrd+QkE2HEhR57Z04m0KtpbsZH73j+a3F8UD1TQnn+ExTvIA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4971,7 +6341,7 @@ "node_modules/binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", "dev": true, "dependencies": { "buffers": "~0.1.1", @@ -5040,13 +6410,13 @@ "node_modules/bluebird": { "version": "3.4.7", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", "dev": true }, "node_modules/body": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", + "integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==", "dev": true, "dependencies": { "continuable-cache": "^0.3.1", @@ -5056,23 +6426,26 @@ } }, "node_modules/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/body-parser/node_modules/debug": { @@ -5086,18 +6459,18 @@ "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/body/node_modules/bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", + "integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==", "dev": true }, "node_modules/body/node_modules/raw-body": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", + "integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==", "dev": true, "dependencies": { "bytes": "1", @@ -5110,7 +6483,7 @@ "node_modules/body/node_modules/string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true }, "node_modules/brace-expansion": { @@ -5142,9 +6515,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "funding": [ { "type": "opencollective", @@ -5156,10 +6529,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -5178,12 +6551,13 @@ } }, "node_modules/browserstack-local": { - "version": "1.4.9", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.9.tgz", - "integrity": "sha512-V+q8HQwRQFr9nd32xR66ZZ3VDWa3Kct4IMMudhKgcuD7cWrvvFARZOibx71II+Rf7P5nMQpWWxl9z/3p927nbg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.5.1.tgz", + "integrity": "sha512-T/wxyWDzvBHbDvl7fZKpFU7mYze6nrUkBhNy+d+8bXBqgQX10HTYvajIGO0wb49oGSLCPM0CMZTV/s7e6LF0sA==", "dev": true, "dependencies": { - "https-proxy-agent": "^4.0.0", + "agent-base": "^6.0.2", + "https-proxy-agent": "^5.0.1", "is-running": "^2.1.0", "ps-tree": "=1.2.0", "temp-fs": "^0.9.9" @@ -5224,9 +6598,9 @@ } }, "node_modules/browserstacktunnel-wrapper": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.4.tgz", - "integrity": "sha512-GCV599FUUxNOCFl3WgPnfc5dcqq9XTmMXoxWpqkvmk0R9TOIoqmjENNU6LY6DtgIL6WfBVbg/jmWtnM5K6UYSg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.5.tgz", + "integrity": "sha512-oociT3nl+FhQnyJbAb1RM4oQ5pN7aKeXEURkTkiEVm/Rji2r0agl3Wbw5V23VFn9lCU5/fGyDejRZPtGYsEcFw==", "dev": true, "dependencies": { "https-proxy-agent": "^2.2.1", @@ -5297,19 +6671,22 @@ "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "engines": { "node": "*" } }, "node_modules/buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", "dev": true, "engines": { - "node": ">=0.4.0" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/buffer-from": { @@ -5330,7 +6707,7 @@ "node_modules/buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", "dev": true, "engines": { "node": ">=0.2.0" @@ -5347,7 +6724,7 @@ "node_modules/cac": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz", - "integrity": "sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8=", + "integrity": "sha512-hq4rxE3NT5PlaEiVV39Z45d6MoFcQZG5dsgJqtAUeOz3408LEQAElToDkf9i5IYSCOmK0If/81dLg7nKxqPR0w==", "dev": true, "dependencies": { "camelcase-keys": "^3.0.0", @@ -5365,7 +6742,7 @@ "node_modules/cac/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5374,38 +6751,16 @@ "node_modules/cac/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cac/node_modules/camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/cac/node_modules/camelcase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-3.0.0.tgz", - "integrity": "sha1-/AxsNgNj9zd+N5O5oWvM8QcMHKQ=", - "dev": true, - "dependencies": { - "camelcase": "^3.0.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cac/node_modules/chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "dependencies": { "ansi-styles": "^2.2.1", @@ -5421,7 +6776,7 @@ "node_modules/cac/node_modules/find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", "dev": true, "dependencies": { "path-exists": "^2.0.0", @@ -5431,19 +6786,28 @@ "node": ">=0.10.0" } }, - "node_modules/cac/node_modules/indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "node_modules/cac/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/cac/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, "node_modules/cac/node_modules/path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", "dev": true, "dependencies": { "pinkie-promise": "^2.0.0" @@ -5455,7 +6819,7 @@ "node_modules/cac/node_modules/read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", "dev": true, "dependencies": { "load-json-file": "^1.0.0", @@ -5469,7 +6833,7 @@ "node_modules/cac/node_modules/read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", "dev": true, "dependencies": { "find-up": "^1.0.0", @@ -5479,10 +6843,19 @@ "node": ">=0.10.0" } }, + "node_modules/cac/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/cac/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -5494,7 +6867,7 @@ "node_modules/cac/node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "engines": { "node": ">=0.8.0" @@ -5592,6 +6965,28 @@ "node": ">=6" } }, + "node_modules/camelcase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-3.0.0.tgz", + "integrity": "sha512-U4E6A6aFyYnNW+tDt5/yIUKQURKXe3WMFPfX4FxrQFcwZ/R08AUk1xWcUtlr7oq6CV07Ji+aa69V2g7BSpblnQ==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/can-autoplay": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/can-autoplay/-/can-autoplay-3.0.2.tgz", @@ -5599,9 +6994,9 @@ "dev": true }, "node_modules/caniuse-lite": { - "version": "1.0.30001390", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001390.tgz", - "integrity": "sha512-sS4CaUM+/+vqQUlCvCJ2WtDlV81aWtHhqeEVkLokVJJa3ViN4zDxAGfq9R8i1m90uGHxo99cy10Od+lvn3hf0g==", + "version": "1.0.30001429", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001429.tgz", + "integrity": "sha512-511ThLu1hF+5RRRt0zYCf2U2yRr9GPF6m5y90SBCWsvSoYoW7yAGlv/elyPaNfvGCkp6kj/KFZWU0BMA69Prsg==", "funding": [ { "type": "opencollective", @@ -5616,7 +7011,7 @@ "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "node_modules/ccount": { @@ -5650,7 +7045,7 @@ "node_modules/chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", "dev": true, "dependencies": { "traverse": ">=0.3.0 <0.4" @@ -5672,25 +7067,6 @@ "node": ">=4" } }, - "node_modules/chalk/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -5730,7 +7106,7 @@ "node_modules/check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true, "engines": { "node": "*" @@ -5770,9 +7146,9 @@ "dev": true }, "node_modules/chrome-launcher": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.0.tgz", - "integrity": "sha512-ZQqX5kb9H0+jy1OqLnWampfocrtSZaGl7Ny3F9GRha85o4odbL8x55paUzh51UC7cEmZ5obp3H2Mm70uC2PpRA==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.1.tgz", + "integrity": "sha512-UugC8u59/w2AyX5sHLZUHoxBAiSiunUhZa3zZwMH6zPVis0C3dDKiRWyUGIo14tTbZHGVviWxv3PQWZ7taZ4fg==", "dev": true, "dependencies": { "@types/node": "*", @@ -5823,10 +7199,19 @@ "node": ">=0.10.0" } }, + "node_modules/class-utils/node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/class-utils/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "dependencies": { "is-descriptor": "^0.1.0" @@ -5838,7 +7223,7 @@ "node_modules/class-utils/node_modules/is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -5850,7 +7235,7 @@ "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -5868,7 +7253,7 @@ "node_modules/class-utils/node_modules/is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -5880,7 +7265,7 @@ "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -5916,9 +7301,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", "dev": true, "engines": { "node": ">=6" @@ -5937,20 +7322,23 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", "dev": true, "engines": { "node": ">=0.8" @@ -5959,25 +7347,28 @@ "node_modules/clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", "dev": true, "engines": { "node": ">= 0.10" } }, "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "dependencies": { "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", "dev": true }, "node_modules/cloneable-readable": { @@ -5994,7 +7385,7 @@ "node_modules/code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6003,7 +7394,7 @@ "node_modules/collection-map": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", - "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "integrity": "sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==", "dev": true, "dependencies": { "arr-map": "^2.0.2", @@ -6017,7 +7408,7 @@ "node_modules/collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", "dev": true, "dependencies": { "map-visit": "^1.0.0", @@ -6038,7 +7429,7 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/color-support": { "version": "1.1.3", @@ -6080,10 +7471,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "node_modules/component-emitter": { @@ -6124,7 +7521,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/concat-stream": { @@ -6193,12 +7590,51 @@ "ms": "2.0.0" } }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/connect/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -6210,25 +7646,6 @@ "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -6240,21 +7657,18 @@ "node_modules/continuable-cache": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", + "integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA==", "dev": true }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } @@ -6262,12 +7676,12 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6284,10 +7698,9 @@ } }, "node_modules/core-js": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", - "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz", + "integrity": "sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6295,31 +7708,21 @@ } }, "node_modules/core-js-compat": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", - "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.0.tgz", + "integrity": "sha512-piOX9Go+Z4f9ZiBFLnZ5VrOpBl0h7IGCkiFUN11QTe6LjAvOT3ifL/5TdoizMh99hcGy5SoLyWbapIY/PIb/3A==", "dependencies": { - "browserslist": "^4.19.1", - "semver": "7.0.0" + "browserslist": "^4.21.4" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/core-js-pure": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", - "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==", - "deprecated": "core-js-pure@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js-pure.", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.0.tgz", + "integrity": "sha512-LiN6fylpVBVwT8twhhluD9TzXmZQQsr2I2eIKtWNbZI1XMfBT7CV18itaN6RA7EtQd/SDdRx/wzvAShX2HvhQA==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6365,14 +7768,10 @@ } }, "node_modules/crc-32": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.1.tgz", - "integrity": "sha512-Dn/xm/1vFFgs3nfrpEVScHoIslO9NZRITWGz/1E/St6u4xw99vfZzVkW0OSnzx2h9egej9xwMCEut6sqwokM/w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "dev": true, - "dependencies": { - "exit-on-epipe": "~1.0.1", - "printj": "~1.3.1" - }, "bin": { "crc32": "bin/crc32.njs" }, @@ -6460,7 +7859,7 @@ "node_modules/css-value": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", - "integrity": "sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo=", + "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", "dev": true }, "node_modules/css/node_modules/source-map": { @@ -6475,7 +7874,7 @@ "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "dev": true }, "node_modules/d": { @@ -6491,7 +7890,7 @@ "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "dependencies": { "assert-plus": "^1.0.0" @@ -6501,9 +7900,9 @@ } }, "node_modules/date-format": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.4.tgz", - "integrity": "sha512-/jyf4rhB17ge328HJuJjAcmRtCsGd+NDeAtahRBTaK6vSPR6MO5HlrAit3Nn7dVjaa6sowW0WXt8yQtLyZQFRg==", + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", "dev": true, "engines": { "node": ">=4.0" @@ -6512,7 +7911,7 @@ "node_modules/dateformat": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "integrity": "sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==", "dev": true, "engines": { "node": "*" @@ -6526,9 +7925,9 @@ "optional": true }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -6562,12 +7961,15 @@ } }, "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/decode-named-character-reference": { @@ -6586,7 +7988,7 @@ "node_modules/decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", "dev": true, "engines": { "node": ">=0.10" @@ -6687,25 +8089,28 @@ "node_modules/default-resolution": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", - "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", "dev": true, "engines": { "node": ">= 0.10" } }, "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, "dependencies": { "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/defaults/node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, "engines": { "node": ">=0.8" @@ -6721,14 +8126,19 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-property": { @@ -6747,18 +8157,18 @@ "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "engines": { "node": ">=0.4.0" } }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/dequal": { @@ -6771,23 +8181,39 @@ } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true, "bin": { "detect-libc": "bin/detect-libc.js" @@ -6799,46 +8225,237 @@ "node_modules/detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/devtools": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.16.16.tgz", - "integrity": "sha512-M0kzkuSgfEhpqIis3gdtWsNjn/HQ+vRAmEzDnbYx/7FfjFxhSv1d+rOOT20pvd60soItMYpsOova1igACEGkGQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.25.4.tgz", + "integrity": "sha512-R6/S/dCqxoX4Y6PxIGM9JFAuSRZzUeV5r+CoE/frhmno6mTe7dEEgwkJlfit3LkKRoul8n4DsL2A3QtWOvq5IA==", "dev": true, "dependencies": { - "@types/node": "^17.0.4", + "@types/node": "^18.0.0", "@types/ua-parser-js": "^0.7.33", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@wdio/config": "7.25.4", + "@wdio/logger": "7.19.0", + "@wdio/protocols": "7.22.0", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", "chrome-launcher": "^0.15.0", "edge-paths": "^2.1.0", "puppeteer-core": "^13.1.3", "query-selector-shadow-dom": "^1.0.0", "ua-parser-js": "^1.0.1", - "uuid": "^8.0.0" + "uuid": "^9.0.0" }, "engines": { "node": ">=12.0.0" } }, "node_modules/devtools-protocol": { - "version": "0.0.973690", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.973690.tgz", - "integrity": "sha512-myh3hSFp0YWa2GED11PmbLhV4dv9RdO7YUz27XJrbQLnP5bMbZL6dfOOILTHO57yH0kX5GfuOZBsg/4NamfPvQ==", + "version": "0.0.1061995", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1061995.tgz", + "integrity": "sha512-pKZZWTjWa/IF4ENCg6GN8bu/AxSZgdhjSa26uc23wz38Blt2Tnm9icOPcSG3Cht55rMq35in1w3rWVPcZ60ArA==", + "dev": true + }, + "node_modules/devtools/node_modules/@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, + "node_modules/devtools/node_modules/@wdio/config": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.25.4.tgz", + "integrity": "sha512-vb0emDtD9FbFh/yqW6oNdo2iuhQp8XKj6GX9fyy9v4wZgg3B0HPMVJxhIfcoHz7LMBWlHSo9YdvhFI5EQHRLBA==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.19.0", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", + "deepmerge": "^4.0.0", + "glob": "^8.0.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/devtools/node_modules/@wdio/logger": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.19.0.tgz", + "integrity": "sha512-xR7SN/kGei1QJD1aagzxs3KMuzNxdT/7LYYx+lt6BII49+fqL/SO+5X0FDCZD0Ds93AuQvvz9eGyzrBI2FFXmQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/devtools/node_modules/@wdio/protocols": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.22.0.tgz", + "integrity": "sha512-8EXRR+Ymdwousm/VGtW3H1hwxZ/1g1H99A1lF0U4GuJ5cFWHCd0IVE5H31Z52i8ZruouW8jueMkGZPSo2IIUSQ==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/devtools/node_modules/@wdio/types": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.25.4.tgz", + "integrity": "sha512-muvNmq48QZCvocctnbe0URq2FjJjUPIG4iLoeMmyF0AQgdbjaUkMkw3BHYNHVTbSOU9WMsr2z8alhj/I2H6NRQ==", + "dev": true, + "dependencies": { + "@types/node": "^18.0.0", + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "^4.6.2" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/devtools/node_modules/@wdio/utils": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.25.4.tgz", + "integrity": "sha512-8iwQDk+foUqSzKZKfhLxjlCKOkfRJPNHaezQoevNgnrTq/t0ek+ldZCATezb9B8jprAuP4mgS9xi22akc6RkzA==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.19.0", + "@wdio/types": "7.25.4", + "p-iteration": "^1.1.8" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/devtools/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/devtools/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/devtools/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/devtools/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/devtools/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/devtools/node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/devtools/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/devtools/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/devtools/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/devtools/node_modules/ua-parser-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.2.tgz", - "integrity": "sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg==", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.32.tgz", + "integrity": "sha512-dXVsz3M4j+5tTiovFVyVqssXBu5HM47//YSOeZ9fQkdDKkfzv2v3PP1jmH6FUyPW+yCSn7aBVK1fGGKNhowdDA==", "dev": true, "funding": [ { @@ -6855,9 +8472,9 @@ } }, "node_modules/devtools/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "dev": true, "bin": { "uuid": "dist/bin/uuid" @@ -6866,25 +8483,25 @@ "node_modules/di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", "dev": true, "engines": { "node": ">=0.3.1" } }, "node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", "dev": true, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.14.2" } }, "node_modules/dlv": { @@ -6987,9 +8604,9 @@ } }, "node_modules/documentation/node_modules/chalk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", - "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -6998,15 +8615,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/documentation/node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/documentation/node_modules/glob": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", @@ -7026,15 +8634,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/documentation/node_modules/ini": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", - "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/documentation/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -7059,25 +8658,13 @@ "node": ">=10" } }, - "node_modules/documentation/node_modules/strip-json-comments": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.0.tgz", - "integrity": "sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/documentation/node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", + "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", "dev": true, "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", @@ -7092,7 +8679,7 @@ "node_modules/dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", "dev": true, "dependencies": { "custom-event": "~1.0.0", @@ -7122,14 +8709,38 @@ "dev": true }, "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", "dev": true, "dependencies": { - "readable-stream": "^2.0.2" + "readable-stream": "~1.1.9" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, "node_modules/duplexify": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", @@ -7199,7 +8810,7 @@ "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "dependencies": { "jsbn": "~0.1.0", @@ -7219,7 +8830,7 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { "version": "3.1.8", @@ -7237,14 +8848,14 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.243", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.243.tgz", - "integrity": "sha512-BgLD2gBX43OSXwlT01oYRRD5NIB4n3okTRxkzEAC6G0SZG4TTlyrWMjbOo0fajCwqwpRtMHXQNMjtRN6qpNtfw==" + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "node_modules/emojis-list": { @@ -7259,7 +8870,7 @@ "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } @@ -7274,9 +8885,9 @@ } }, "node_modules/engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -7295,21 +8906,27 @@ } }, "node_modules/engine.io-parser": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", - "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", "dev": true, - "dependencies": { - "@socket.io/base64-arraybuffer": "~1.0.2" - }, "engines": { "node": ">=10.0.0" } }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/enhanced-resolve": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", - "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -7334,7 +8951,7 @@ "node_modules/ent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", "dev": true }, "node_modules/errno": { @@ -7368,31 +8985,35 @@ } }, "node_modules/es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.2", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7426,6 +9047,15 @@ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -7459,9 +9089,9 @@ } }, "node_modules/es5-shim": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.5.tgz", - "integrity": "sha512-vfQ4UAai8szn0sAubCy97xnZ4sJVDD1gt/Grn736hg8D7540wemIb1YPrYZSTqlM2H69EQX1or4HU/tSwRTI3w==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.7.tgz", + "integrity": "sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==", "dev": true, "engines": { "node": ">=0.4.0" @@ -7470,7 +9100,7 @@ "node_modules/es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", "dev": true, "dependencies": { "d": "1", @@ -7481,7 +9111,7 @@ "node_modules/es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", "dev": true }, "node_modules/es6-promise": { @@ -7493,7 +9123,7 @@ "node_modules/es6-promisify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", "dev": true, "dependencies": { "es6-promise": "^4.0.3" @@ -7532,12 +9162,12 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { "node": ">=0.8.0" } @@ -7545,7 +9175,7 @@ "node_modules/escodegen": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", "dev": true, "dependencies": { "esprima": "^2.7.1", @@ -7567,7 +9197,7 @@ "node_modules/escodegen/node_modules/estraverse": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -7576,7 +9206,7 @@ "node_modules/escodegen/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, "dependencies": { "prelude-ls": "~1.1.2", @@ -7606,7 +9236,7 @@ "node_modules/escodegen/node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true, "engines": { "node": ">= 0.8.0" @@ -7615,7 +9245,7 @@ "node_modules/escodegen/node_modules/source-map": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", "dev": true, "optional": true, "dependencies": { @@ -7628,7 +9258,7 @@ "node_modules/escodegen/node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "dependencies": { "prelude-ls": "~1.1.2" @@ -7697,7 +9327,7 @@ "node_modules/eslint-config-standard": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", - "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=", + "integrity": "sha512-UkFojTV1o0GOe1edOEiuI5ccYLJSuNngtqSeClNzhsmG8KPJ+7mRxgtp2oYhqZAK/brlXMoCd+VgXViE0AfyKw==", "dev": true, "peerDependencies": { "eslint": ">=3.19.0", @@ -7727,16 +9357,20 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", - "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", "dev": true, "dependencies": { - "debug": "^3.2.7", - "find-up": "^2.1.0" + "debug": "^3.2.7" }, "engines": { "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, "node_modules/eslint-module-utils/node_modules/debug": { @@ -7768,9 +9402,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "dependencies": { "array-includes": "^3.1.4", @@ -7778,14 +9412,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "is-glob": "^4.0.3", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "engines": { "node": ">=4" @@ -7818,7 +9452,7 @@ "node_modules/eslint-plugin-import/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "node_modules/eslint-plugin-node": { @@ -7930,22 +9564,6 @@ "@babel/highlight": "^7.10.4" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -8008,9 +9626,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -8022,10 +9640,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/eslint/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -8099,7 +9726,7 @@ "node_modules/esprima": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", "dev": true, "bin": { "esparse": "bin/esparse.js", @@ -8178,7 +9805,7 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } @@ -8186,7 +9813,7 @@ "node_modules/event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", "dev": true, "dependencies": { "d": "1", @@ -8196,7 +9823,7 @@ "node_modules/event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", "dev": true, "dependencies": { "duplexer": "~0.1.1", @@ -8211,7 +9838,7 @@ "node_modules/event-stream/node_modules/map-stream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", "dev": true }, "node_modules/eventemitter3": { @@ -8266,7 +9893,7 @@ "node_modules/execa/node_modules/path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "engines": { "node": ">=4" @@ -8284,7 +9911,7 @@ "node_modules/execa/node_modules/shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "dependencies": { "shebang-regex": "^1.0.0" @@ -8296,7 +9923,7 @@ "node_modules/execa/node_modules/shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -8314,19 +9941,10 @@ "which": "bin/which" } }, - "node_modules/exit-on-epipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", - "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", "dev": true, "dependencies": { "debug": "^2.3.3", @@ -8353,7 +9971,7 @@ "node_modules/expand-brackets/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "dependencies": { "is-descriptor": "^0.1.0" @@ -8365,7 +9983,7 @@ "node_modules/expand-brackets/node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "dependencies": { "is-extendable": "^0.1.0" @@ -8377,7 +9995,7 @@ "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -8389,7 +10007,7 @@ "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -8407,7 +10025,7 @@ "node_modules/expand-brackets/node_modules/is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -8419,7 +10037,7 @@ "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -8445,7 +10063,7 @@ "node_modules/expand-brackets/node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -8454,13 +10072,13 @@ "node_modules/expand-brackets/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, "dependencies": { "homedir-polyfill": "^1.0.1" @@ -8470,62 +10088,98 @@ } }, "node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", "dev": true, "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.14.2" } }, "node_modules/expect-webdriverio": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-3.1.4.tgz", - "integrity": "sha512-65FTS3bmxcIp0cq6fLb/72TrCQXBCpwPLC7SwMWdpPlLq461mXcK1BPKJJjnIC587aXSKD+3E4hvnlCtwDmXfg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-2.0.2.tgz", + "integrity": "sha512-dst0tqP1aZ2p7TPmbatqoIQ+7hRTw+IeKNi830XxKhu2DNNe5vQ85i9ttf9rpXgbnUf91HxKcocn4G7A5bQxDA==", "dev": true, "dependencies": { - "expect": "^27.0.2", - "jest-matcher-utils": "^27.0.2" + "expect": "^26.6.2", + "jest-matcher-utils": "^26.6.2" } }, + "node_modules/expect/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expect/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/expect/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.2", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.2", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.7", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", + "send": "0.18.0", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -8545,40 +10199,21 @@ "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", "dev": true, "dependencies": { - "type": "^2.5.0" + "type": "^2.7.2" } }, "node_modules/ext/node_modules/type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", "dev": true }, "node_modules/extend": { @@ -8588,18 +10223,26 @@ "dev": true }, "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", "dev": true, "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "kind-of": "^1.1.0" }, "engines": { "node": ">=0.10.0" } }, + "node_modules/extend-shallow/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -8614,18 +10257,6 @@ "node": ">=4" } }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -8648,7 +10279,7 @@ "node_modules/extglob/node_modules/define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, "dependencies": { "is-descriptor": "^1.0.0" @@ -8660,7 +10291,7 @@ "node_modules/extglob/node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "dependencies": { "is-extendable": "^0.1.0" @@ -8672,7 +10303,7 @@ "node_modules/extglob/node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -8716,7 +10347,7 @@ "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true, "engines": [ "node >=0.6.0" @@ -8758,13 +10389,13 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", "dev": true, "dependencies": { "websocket-driver": ">=0.5.1" @@ -8776,16 +10407,16 @@ "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "dependencies": { "pend": "~1.2.0" } }, "node_modules/fibers": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.1.tgz", - "integrity": "sha512-VMC7Frt87Oo0AOJ6EcPFbi+tZmkQ4tD85aatwyWL6I9cYMJmm2e+pXUJsfGZ36U7MffXtjou2XIiWJMtHriErw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.3.tgz", + "integrity": "sha512-/qYTSoZydQkM21qZpGLDLuCq8c+B8KhuCQ1kLPvnRNhxhVbvrpmH9l2+Lblf5neDuEsY4bfT7LeO553TXQDvJw==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -8872,16 +10503,16 @@ } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -8899,7 +10530,7 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/find-cache-dir": { "version": "3.3.2", @@ -8919,15 +10550,16 @@ } }, "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "locate-path": "^2.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/findup-sync": { @@ -8945,6 +10577,182 @@ "node": ">= 0.10" } }, + "node_modules/findup-sync/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/braces/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/fill-range/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/findup-sync/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fined": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", @@ -9005,9 +10813,9 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "node_modules/flush-write-stream": { @@ -9021,9 +10829,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true, "funding": [ { @@ -9040,10 +10848,19 @@ } } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9052,7 +10869,7 @@ "node_modules/for-own": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", "dev": true, "dependencies": { "for-in": "^1.0.1" @@ -9061,22 +10878,16 @@ "node": ">=0.10.0" } }, - "node_modules/foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, "node_modules/foreachasync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", - "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=", + "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==", "dev": true }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, "engines": { "node": "*" @@ -9085,7 +10896,7 @@ "node_modules/fork-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", - "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", + "integrity": "sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==", "dev": true }, "node_modules/form-data": { @@ -9113,7 +10924,7 @@ "node_modules/fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", "dev": true, "dependencies": { "map-cache": "^0.2.2" @@ -9125,7 +10936,7 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } @@ -9133,7 +10944,7 @@ "node_modules/from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", "dev": true }, "node_modules/fs-constants": { @@ -9143,9 +10954,9 @@ "dev": true }, "node_modules/fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -9159,7 +10970,7 @@ "node_modules/fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", - "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", "dev": true, "dependencies": { "graceful-fs": "^4.1.11", @@ -9182,7 +10993,7 @@ "node_modules/fs.extra": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz", - "integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=", + "integrity": "sha512-Ig401VXtyrWrz23k9KxAx9OrnL8AHSLNhQ8YJH2wSYuH0ZUfxwBeY6zXkd/oOyVRFTlpEu/0n5gHeuZt7aqbkw==", "dev": true, "dependencies": { "fs-extra": "~0.6.1", @@ -9196,7 +11007,7 @@ "node_modules/fs.extra/node_modules/fs-extra": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", - "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", + "integrity": "sha512-5rU898vl/Z948L+kkJedbmo/iltzmiF5bn/eEk0j/SgrPpI+Ydau9xlJPicV7Av2CHYBGz5LAlwTnBU80j1zPQ==", "dev": true, "dependencies": { "jsonfile": "~1.0.1", @@ -9208,20 +11019,20 @@ "node_modules/fs.extra/node_modules/jsonfile": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", - "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=", + "integrity": "sha512-KbsDJNRfRPF5v49tMNf9sqyyGqGLBcz1v5kZT01kG5ns5mQSltwxCKVmUzVKtEinkUnTDtSrp6ngWpV7Xw0ZlA==", "dev": true }, "node_modules/fs.extra/node_modules/mkdirp": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", + "integrity": "sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==", "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", "dev": true }, "node_modules/fs.extra/node_modules/rimraf": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "integrity": "sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==", "dev": true, "bin": { "rimraf": "bin.js" @@ -9230,7 +11041,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/fsevents": { @@ -9262,12 +11073,12 @@ } }, "node_modules/fstream/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" @@ -9298,12 +11109,39 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gaze": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", @@ -9336,20 +11174,20 @@ "node_modules/get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true, "engines": { "node": "*" } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9407,7 +11245,7 @@ "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9416,7 +11254,7 @@ "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "dependencies": { "assert-plus": "^1.0.0" @@ -9448,15 +11286,15 @@ "dev": true }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -9513,7 +11351,7 @@ "node_modules/glob-stream/node_modules/is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, "dependencies": { "is-extglob": "^2.1.0" @@ -9559,7 +11397,7 @@ "node_modules/glob-watcher/node_modules/anymatch/node_modules/normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, "dependencies": { "remove-trailing-separator": "^1.0.1" @@ -9568,6 +11406,15 @@ "node": ">=0.10.0" } }, + "node_modules/glob-watcher/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/glob-watcher/node_modules/binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", @@ -9624,7 +11471,7 @@ "node_modules/glob-watcher/node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "dependencies": { "is-extendable": "^0.1.0" @@ -9636,7 +11483,7 @@ "node_modules/glob-watcher/node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", "dev": true, "dependencies": { "extend-shallow": "^2.0.1", @@ -9680,7 +11527,7 @@ "node_modules/glob-watcher/node_modules/glob-parent/node_modules/is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, "dependencies": { "is-extglob": "^2.1.0" @@ -9692,7 +11539,7 @@ "node_modules/glob-watcher/node_modules/is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", "dev": true, "dependencies": { "binary-extensions": "^1.0.0" @@ -9701,10 +11548,110 @@ "node": ">=0.10.0" } }, + "node_modules/glob-watcher/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "node_modules/glob-watcher/node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/micromatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9727,7 +11674,7 @@ "node_modules/glob-watcher/node_modules/to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", "dev": true, "dependencies": { "is-number": "^3.0.0", @@ -9764,7 +11711,7 @@ "node_modules/global-prefix": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, "dependencies": { "expand-tilde": "^2.0.2", @@ -9777,6 +11724,12 @@ "node": ">=0.10.0" } }, + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "node_modules/global-prefix/node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -9804,13 +11757,13 @@ "dev": true }, "node_modules/globule": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", - "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", + "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", "dev": true, "dependencies": { "glob": "~7.1.1", - "lodash": "~4.17.10", + "lodash": "^4.17.21", "minimatch": "~3.0.2" }, "engines": { @@ -9887,9 +11840,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "node_modules/grapheme-splitter": { @@ -9941,74 +11894,6 @@ "node": ">=0.9" } }, - "node_modules/gulp-clean/node_modules/arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", - "dev": true, - "dependencies": { - "kind-of": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", - "dev": true, - "dependencies": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/gulp-clean/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -10078,7 +11963,7 @@ "node_modules/gulp-cli/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -10087,7 +11972,7 @@ "node_modules/gulp-cli/node_modules/camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -10096,7 +11981,7 @@ "node_modules/gulp-cli/node_modules/cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", "dev": true, "dependencies": { "string-width": "^1.0.1", @@ -10104,10 +11989,19 @@ "wrap-ansi": "^2.0.0" } }, + "node_modules/gulp-cli/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gulp-cli/node_modules/find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", "dev": true, "dependencies": { "path-exists": "^2.0.0", @@ -10123,10 +12017,16 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "node_modules/gulp-cli/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, "node_modules/gulp-cli/node_modules/is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", "dev": true, "dependencies": { "number-is-nan": "^1.0.0" @@ -10135,10 +12035,22 @@ "node": ">=0.10.0" } }, + "node_modules/gulp-cli/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "node_modules/gulp-cli/node_modules/path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", "dev": true, "dependencies": { "pinkie-promise": "^2.0.0" @@ -10150,7 +12062,7 @@ "node_modules/gulp-cli/node_modules/read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", "dev": true, "dependencies": { "load-json-file": "^1.0.0", @@ -10164,7 +12076,7 @@ "node_modules/gulp-cli/node_modules/read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", "dev": true, "dependencies": { "find-up": "^1.0.0", @@ -10174,16 +12086,19 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-cli/node_modules/require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true + "node_modules/gulp-cli/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } }, "node_modules/gulp-cli/node_modules/string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", "dev": true, "dependencies": { "code-point-at": "^1.0.0", @@ -10197,7 +12112,7 @@ "node_modules/gulp-cli/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -10206,16 +12121,10 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-cli/node_modules/which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, "node_modules/gulp-cli/node_modules/wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", "dev": true, "dependencies": { "string-width": "^1.0.1", @@ -10265,7 +12174,7 @@ "node_modules/gulp-concat": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", - "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "integrity": "sha512-a2scActrQrDBpBbR3WUZGyGS1JEPLg5PZJdIa7/Bi3GuKAmPYDK6SFhy/NZq5R8KsKKFvtfR0fakbUCcKGCCjg==", "dev": true, "dependencies": { "concat-with-sourcemaps": "^1.0.0", @@ -10324,10 +12233,25 @@ "ms": "2.0.0" } }, + "node_modules/gulp-connect/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/gulp-connect/node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true + }, "node_modules/gulp-connect/node_modules/http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "dependencies": { "depd": "~1.1.2", @@ -10342,7 +12266,7 @@ "node_modules/gulp-connect/node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, "node_modules/gulp-connect/node_modules/mime": { @@ -10357,9 +12281,21 @@ "node_modules/gulp-connect/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/gulp-connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/gulp-connect/node_modules/send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -10410,6 +12346,18 @@ "plugin-error": "^1.0.1" } }, + "node_modules/gulp-eslint/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gulp-eslint/node_modules/ansi-regex": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", @@ -10419,6 +12367,24 @@ "node": ">=6" } }, + "node_modules/gulp-eslint/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-eslint/node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gulp-eslint/node_modules/astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -10552,22 +12518,6 @@ "node": ">=4" } }, - "node_modules/gulp-eslint/node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/gulp-eslint/node_modules/eslint/node_modules/strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -10594,6 +12544,19 @@ "node": ">=6.0.0" } }, + "node_modules/gulp-eslint/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gulp-eslint/node_modules/file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", @@ -10641,6 +12604,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gulp-eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/gulp-eslint/node_modules/inquirer": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", @@ -10756,6 +12728,21 @@ "node": ">=4" } }, + "node_modules/gulp-eslint/node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/gulp-eslint/node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -10786,18 +12773,6 @@ "rimraf": "bin.js" } }, - "node_modules/gulp-eslint/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, "node_modules/gulp-eslint/node_modules/shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -10955,7 +12930,7 @@ "node_modules/gulp-js-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gulp-js-escape/-/gulp-js-escape-1.0.1.tgz", - "integrity": "sha1-HNRF+9AJ4Np2lZoDp/SbNWav+Gg=", + "integrity": "sha512-F+53crhLb78CTlG7ZZJFWzP0+/4q0vt2/pULXFkTMs6AGBo0Eh5cx+eWsqqHv8hrNIUsuTab3Se8rOOzP/6+EQ==", "dev": true, "dependencies": { "through2": "^0.6.3" @@ -10964,13 +12939,13 @@ "node_modules/gulp-js-escape/node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "node_modules/gulp-js-escape/node_modules/readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", @@ -10982,13 +12957,13 @@ "node_modules/gulp-js-escape/node_modules/string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true }, "node_modules/gulp-js-escape/node_modules/through2": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", "dev": true, "dependencies": { "readable-stream": ">=1.0.33-1 <1.1.0-0", @@ -11021,9 +12996,9 @@ } }, "node_modules/gulp-replace/node_modules/@types/node": { - "version": "14.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", - "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", + "version": "14.18.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", + "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==", "dev": true }, "node_modules/gulp-shell": { @@ -11043,6 +13018,18 @@ "node": ">=10.0.0" } }, + "node_modules/gulp-shell/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gulp-shell/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -11058,6 +13045,24 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/gulp-shell/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-shell/node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gulp-shell/node_modules/chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -11089,6 +13094,43 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/gulp-shell/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-shell/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-shell/node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/gulp-shell/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11179,6 +13221,64 @@ "node": ">=10" } }, + "node_modules/gulp-terser/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-terser/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-terser/node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-terser/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-terser/node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/gulp-util": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", @@ -11212,7 +13312,7 @@ "node_modules/gulp-util/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -11221,7 +13321,7 @@ "node_modules/gulp-util/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -11230,7 +13330,7 @@ "node_modules/gulp-util/node_modules/chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "dependencies": { "ansi-styles": "^2.2.1", @@ -11246,7 +13346,7 @@ "node_modules/gulp-util/node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, "engines": { "node": ">=0.8" @@ -11255,18 +13355,9 @@ "node_modules/gulp-util/node_modules/clone-stats": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", "dev": true }, - "node_modules/gulp-util/node_modules/lodash.escape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", - "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", - "dev": true, - "dependencies": { - "lodash._root": "^3.0.0" - } - }, "node_modules/gulp-util/node_modules/lodash.template": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", @@ -11287,7 +13378,7 @@ "node_modules/gulp-util/node_modules/lodash.templatesettings": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "integrity": "sha512-TcrlEr31tDYnWkHFWDCV3dHYroKEXpJZ2YJYvJdhN+y4AkWMDZ5I4I8XDtUKqSAyG81N7w+I1mFEJtcED+tGqQ==", "dev": true, "dependencies": { "lodash._reinterpolate": "^3.0.0", @@ -11297,7 +13388,7 @@ "node_modules/gulp-util/node_modules/object-assign": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -11306,7 +13397,7 @@ "node_modules/gulp-util/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -11318,7 +13409,7 @@ "node_modules/gulp-util/node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "engines": { "node": ">=0.8.0" @@ -11337,7 +13428,7 @@ "node_modules/gulp-util/node_modules/vinyl": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "integrity": "sha512-P5zdf3WB9uzr7IFoVQ2wZTmUwHL8cMZWJGzLBNCHNZ3NB6HTMsYABtt7z8tAGIINLXyAob9B9a1yzVGMFOYKEA==", "dev": true, "dependencies": { "clone": "^1.0.0", @@ -11351,7 +13442,7 @@ "node_modules/gulplog": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "integrity": "sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==", "dev": true, "dependencies": { "glogg": "^1.0.0" @@ -11408,7 +13499,7 @@ "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "dev": true, "engines": { "node": ">=4" @@ -11428,22 +13519,6 @@ "node": ">=6" } }, - "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -11458,7 +13533,7 @@ "node_modules/has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -11470,34 +13545,33 @@ "node_modules/has-ansi/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/has-gulplog": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "integrity": "sha512-+F4GzLjwHNNDEAJW2DC1xXfEoPkRDmUdJ7CBYw4MpqtDwOnqdImJl7GWlpqx+Wko6//J8uKTnIe4wZSv7yCqmw==", "dev": true, "dependencies": { "sparkles": "^1.0.0" @@ -11506,6 +13580,18 @@ "node": ">= 0.10" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -11535,7 +13621,7 @@ "node_modules/has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", "dev": true, "dependencies": { "get-value": "^2.0.6", @@ -11549,7 +13635,7 @@ "node_modules/has-values": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", "dev": true, "dependencies": { "is-number": "^3.0.0", @@ -11565,10 +13651,34 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/has-values/node_modules/kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -11657,7 +13767,7 @@ "node_modules/home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "integrity": "sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==", "dev": true, "dependencies": { "os-homedir": "^1.0.0", @@ -11680,10 +13790,16 @@ } }, "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } }, "node_modules/html-escaper": { "version": "2.0.2", @@ -11708,24 +13824,24 @@ "dev": true }, "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "depd": "~1.1.2", + "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", + "statuses": "2.0.1", "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/http-parser-js": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", - "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "dev": true }, "node_modules/http-proxy": { @@ -11745,7 +13861,7 @@ "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, "dependencies": { "assert-plus": "^1.0.0", @@ -11771,16 +13887,16 @@ } }, "node_modules/https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { - "agent-base": "5", + "agent-base": "6", "debug": "4" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 6" } }, "node_modules/iconv-lite": { @@ -11851,12 +13967,21 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/individual": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz", @@ -11866,7 +13991,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -11879,15 +14004,18 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } }, "node_modules/inquirer": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz", - "integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", "dev": true, "dependencies": { "ansi-escapes": "^4.2.1", @@ -11900,13 +14028,14 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.2.0", + "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" } }, "node_modules/inquirer/node_modules/ansi-styles": { @@ -11958,6 +14087,24 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/inquirer/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11970,6 +14117,12 @@ "node": ">=8" } }, + "node_modules/inquirer/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true + }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -12005,7 +14158,7 @@ "node_modules/invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12072,7 +14225,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "node_modules/is-bigint": { @@ -12139,9 +14292,9 @@ } }, "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" @@ -12151,9 +14304,9 @@ } }, "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dependencies": { "has": "^1.0.3" }, @@ -12262,7 +14415,7 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12359,7 +14512,7 @@ "node_modules/is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12378,21 +14531,18 @@ } }, "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { - "node": ">=0.10.0" + "node": ">=0.12.0" } }, "node_modules/is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" @@ -12404,24 +14554,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -12480,7 +14612,7 @@ "node_modules/is-running": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", - "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", + "integrity": "sha512-mjJd3PujZMl7j+D395WTIO5tU5RIDBfVSRtRR4VOJou3H66E38UjbjvDGh3slJzPuolsb+yQFqwHNNdyp5jg3w==", "dev": true }, "node_modules/is-set": { @@ -12493,10 +14625,13 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12513,7 +14648,7 @@ "node_modules/is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12550,15 +14685,15 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", "has-tostringtag": "^1.0.0" }, "engines": { @@ -12571,7 +14706,7 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "node_modules/is-unc-path": { @@ -12601,13 +14736,13 @@ "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", "dev": true }, "node_modules/is-valid-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", - "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12675,9 +14810,9 @@ "dev": true }, "node_modules/isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, "engines": { "node": ">= 8.0.0" @@ -12689,13 +14824,13 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12704,13 +14839,13 @@ "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "node_modules/istanbul": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "integrity": "sha512-nMtdn4hvK0HjUlzr1DrKSUY8ychprt8dzHOgY2KXsIhHu5PuQQEOTM27gV9Xblyon7aUH/TSFIjRHEODF/FRPg==", "deprecated": "This module is no longer maintained, try this instead:\n npm i nyc\nVisit https://istanbul.js.org/integrations for other alternatives.", "dev": true, "dependencies": { @@ -12742,6 +14877,22 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -12756,6 +14907,15 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12792,9 +14952,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -12807,7 +14967,7 @@ "node_modules/istanbul/node_modules/glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", "dev": true, "dependencies": { "inflight": "^1.0.4", @@ -12823,19 +14983,19 @@ "node_modules/istanbul/node_modules/has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/istanbul/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" @@ -12844,13 +15004,13 @@ "node_modules/istanbul/node_modules/resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", "dev": true }, "node_modules/istanbul/node_modules/supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", "dev": true, "dependencies": { "has-flag": "^1.0.0" @@ -12960,6 +15120,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/jake/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12973,18 +15142,18 @@ } }, "node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.14.2" } }, "node_modules/jest-diff/node_modules/ansi-styles": { @@ -13036,6 +15205,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-diff/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13049,27 +15227,27 @@ } }, "node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.14.2" } }, "node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.14.2" } }, "node_modules/jest-matcher-utils/node_modules/ansi-styles": { @@ -13121,6 +15299,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-matcher-utils/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13134,23 +15321,23 @@ } }, "node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "stack-utils": "^2.0.2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.14.2" } }, "node_modules/jest-message-util/node_modules/ansi-styles": { @@ -13202,17 +15389,22 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/jest-message-util/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, "engines": { - "node": ">=8.6" + "node": ">=8" } }, "node_modules/jest-message-util/node_modules/supports-color": { @@ -13227,6 +15419,15 @@ "node": ">=8" } }, + "node_modules/jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -13241,6 +15442,30 @@ "node": ">= 10.13.0" } }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -13275,7 +15500,7 @@ "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, "node_modules/jsesc": { @@ -13295,12 +15520,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -13322,13 +15541,13 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "node_modules/json5": { @@ -13372,7 +15591,7 @@ "node_modules/just-clone": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-1.0.2.tgz", - "integrity": "sha1-v7P672WqEqMWBYcSlFwyb9jwFDQ=" + "integrity": "sha512-p93GINPwrve0w3HUzpXmpTl7MyzzWz1B5ag44KEtq/hP1mtK8lA2b9Q0VQaPlnY87352osJcE6uBmN0e8kuFMw==" }, "node_modules/just-debounce": { "version": "1.1.0", @@ -13387,9 +15606,9 @@ "dev": true }, "node_modules/karma": { - "version": "6.3.17", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz", - "integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", + "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==", "dev": true, "dependencies": { "@colors/colors": "1.5.0", @@ -13411,7 +15630,7 @@ "qjobs": "^1.2.0", "range-parser": "^1.2.1", "rimraf": "^3.0.2", - "socket.io": "^4.2.0", + "socket.io": "^4.4.1", "source-map": "^0.6.1", "tmp": "^0.2.1", "ua-parser-js": "^0.7.30", @@ -13453,7 +15672,7 @@ "node_modules/karma-chai": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/karma-chai/-/karma-chai-0.1.0.tgz", - "integrity": "sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=", + "integrity": "sha512-mqKCkHwzPMhgTYca10S90aCEX9+HjVjjrBFAsw36Zj7BlQNbokXXCAe6Ji04VUMsxcY5RLP7YphpfO06XOubdg==", "dev": true, "peerDependencies": { "chai": "*", @@ -13461,9 +15680,9 @@ } }, "node_modules/karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", + "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", "dev": true, "dependencies": { "which": "^1.2.1" @@ -13591,26 +15810,10 @@ "node": ">=0.10.0" } }, - "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/karma-es5-shim": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/karma-es5-shim/-/karma-es5-shim-0.0.4.tgz", - "integrity": "sha1-zdADM8znfC5M4D46yT8vjs0fuVI=", + "integrity": "sha512-8xU6F2/R6u6HAZ/nlyhhx3WEhj4C6hJorG7FR2REX81pgj2LSo9ADJXxCGIeXg6Qr2BGpxp4hcZcEOYGAwiumg==", "dev": true, "dependencies": { "es5-shim": "^4.0.5" @@ -13629,7 +15832,7 @@ "node_modules/karma-ie-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-ie-launcher/-/karma-ie-launcher-1.0.0.tgz", - "integrity": "sha1-SXmGhCxJAZA0bNifVJTKmDDG1Zw=", + "integrity": "sha512-ts71ke8pHvw6qdRtq0+7VY3ANLoZuUNNkA8abRaWV13QRPNm7TtSOqyszjHUtuwOWKcsSz4tbUtrNICrQC+SXQ==", "dev": true, "dependencies": { "lodash": "^4.6.1" @@ -13650,7 +15853,7 @@ "node_modules/karma-mocha-reporter": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz", - "integrity": "sha1-FRIAlejtgZGG5HoLAS8810GJVWA=", + "integrity": "sha512-Hr6nhkIp0GIJJrvzY8JFeHpQZNseuIakGac4bpw8K1+5F0tLb6l7uvXRa8mt2Z+NVwYgCct4QAfp2R2QP6o00w==", "dev": true, "dependencies": { "chalk": "^2.1.0", @@ -13673,7 +15876,7 @@ "node_modules/karma-mocha-reporter/node_modules/strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "dependencies": { "ansi-regex": "^3.0.0" @@ -13685,7 +15888,7 @@ "node_modules/karma-opera-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-opera-launcher/-/karma-opera-launcher-1.0.0.tgz", - "integrity": "sha1-+lFihTGh0L6EstjcDX7iCfyP+Ro=", + "integrity": "sha512-rdty4FlVIowmUhPuG08TeXKHvaRxeDSzPxGIkWguCF3A32kE0uvXZ6dXW08PuaNjai8Ip3f5Pn9Pm2HlChaxCw==", "dev": true, "peerDependencies": { "karma": ">=0.9" @@ -13694,7 +15897,7 @@ "node_modules/karma-safari-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz", - "integrity": "sha1-lpgqLMR9BmquccVTursoMZEVos4=", + "integrity": "sha512-qmypLWd6F2qrDJfAETvXDfxHvKDk+nyIjpH9xIeI3/hENr0U3nuqkxaftq73PfXZ4aOuOChA6SnLW4m4AxfRjQ==", "dev": true, "peerDependencies": { "karma": ">=0.9" @@ -13703,7 +15906,7 @@ "node_modules/karma-script-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz", - "integrity": "sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0=", + "integrity": "sha512-5NRc8KmTBjNPE3dNfpJP90BArnBohYV4//MsLFfUA1e6N+G1/A5WuWctaFBtMQ6MWRybs/oguSej0JwDr8gInA==", "dev": true, "peerDependencies": { "karma": ">=0.9" @@ -13712,7 +15915,7 @@ "node_modules/karma-sinon": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/karma-sinon/-/karma-sinon-1.0.5.tgz", - "integrity": "sha1-TjRD8oMP3s/2JNN0cWPxIX2qKpo=", + "integrity": "sha512-wrkyAxJmJbn75Dqy17L/8aILJWFm7znd1CE8gkyxTBFnjMSOe2XTJ3P30T8SkxWZHmoHX0SCaUJTDBEoXs25Og==", "dev": true, "engines": { "node": ">= 0.10.0" @@ -13734,7 +15937,7 @@ "node_modules/karma-spec-reporter": { "version": "0.0.32", "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz", - "integrity": "sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo=", + "integrity": "sha512-ZXsYERZJMTNRR2F3QN11OWF5kgnT/K2dzhM+oY3CDyMrDI3TjIWqYGG7c15rR9wjmy9lvdC+CCshqn3YZqnNrA==", "dev": true, "dependencies": { "colors": "^1.1.2" @@ -13760,13 +15963,24 @@ "webpack": "^5.0.0" } }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/karma/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" @@ -13781,6 +15995,18 @@ "node": ">=0.10.0" } }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, "node_modules/karma/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -13815,9 +16041,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", - "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", + "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -13866,7 +16092,7 @@ "node_modules/last-run": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", - "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "integrity": "sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==", "dev": true, "dependencies": { "default-resolution": "^2.0.0", @@ -13891,7 +16117,7 @@ "node_modules/lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", "dev": true, "dependencies": { "invert-kv": "^1.0.0" @@ -13903,7 +16129,7 @@ "node_modules/lcov-parse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=", + "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", "dev": true, "bin": { "lcov-parse": "bin/cli.js" @@ -13912,7 +16138,7 @@ "node_modules/lead": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", "dev": true, "dependencies": { "flush-write-stream": "^1.0.2" @@ -13987,7 +16213,7 @@ "node_modules/lighthouse-logger/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "node_modules/lines-and-columns": { @@ -13999,7 +16225,7 @@ "node_modules/listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", "dev": true }, "node_modules/live-connect-js": { @@ -14069,25 +16295,38 @@ } }, "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, "engines": { "node": ">=6.11.5" } }, + "node_modules/loader-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/lodash": { @@ -14099,126 +16338,135 @@ "node_modules/lodash._basecopy": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "integrity": "sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ==", "dev": true }, "node_modules/lodash._basetostring": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", - "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "integrity": "sha512-mTzAr1aNAv/i7W43vOR/uD/aJ4ngbtsRaCubp2BfZhlGU/eORUjg/7F6X0orNMdv33JOrdgGybtvMN/po3EWrA==", "dev": true }, "node_modules/lodash._basevalues": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", - "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "integrity": "sha512-H94wl5P13uEqlCg7OcNNhMQ8KvWSIyqXzOPusRgHC9DK3o54P6P3xtbXlVbRABG4q5gSmp7EDdJ0MSuW9HX6Mg==", "dev": true }, "node_modules/lodash._getnative": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==", "dev": true }, "node_modules/lodash._isiterateecall": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==", "dev": true }, "node_modules/lodash._reescape": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", - "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "integrity": "sha512-Sjlavm5y+FUVIF3vF3B75GyXrzsfYV8Dlv3L4mEpuB9leg8N6yf/7rU06iLPx9fY0Mv3khVp9p7Dx0mGV6V5OQ==", "dev": true }, "node_modules/lodash._reevaluate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", - "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "integrity": "sha512-OrPwdDc65iJiBeUe5n/LIjd7Viy99bKwDdk7Z5ljfZg0uFRFlfQaCy9tZ4YMAag9WAZmlVpe1iZrkIMMSMHD3w==", "dev": true }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", "dev": true }, "node_modules/lodash._root": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==", "dev": true }, "node_modules/lodash.clone": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", - "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", + "integrity": "sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==", "dev": true }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "dev": true }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true }, "node_modules/lodash.difference": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", "dev": true }, + "node_modules/lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha512-n1PZMXgaaDWZDSvuNZ/8XOcYO2hOKDqZel5adtR30VKQAtoWs/5AOeFA0vPV8moiPzlqe7F4cP2tzpFewQyelQ==", + "dev": true, + "dependencies": { + "lodash._root": "^3.0.0" + } + }, "node_modules/lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", "dev": true }, "node_modules/lodash.isarray": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", "dev": true }, "node_modules/lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", "dev": true }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, "node_modules/lodash.keys": { @@ -14241,19 +16489,19 @@ "node_modules/lodash.pickby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=", + "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", "dev": true }, "node_modules/lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "integrity": "sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==", "dev": true }, "node_modules/lodash.some": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", "dev": true }, "node_modules/lodash.template": { @@ -14278,19 +16526,19 @@ "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "node_modules/lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", "dev": true }, "node_modules/lodash.zip": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", - "integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", "dev": true }, "node_modules/log-driver": { @@ -14315,16 +16563,16 @@ } }, "node_modules/log4js": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.2.tgz", - "integrity": "sha512-k80cggS2sZQLBwllpT1p06GtfvzMmSdUCkW96f0Hj83rKGJDAu2vZjt9B9ag2vx8Zz1IXzxoLgqvRJCdMKybGg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.0.tgz", + "integrity": "sha512-KA0W9ffgNBLDj6fZCq/lRbgR6ABAodRIDHrZnS48vOtfKa4PzWImb0Md1lmGCdO3n3sbCm/n1/WmrNlZ8kCI3Q==", "dev": true, "dependencies": { - "date-format": "^4.0.4", - "debug": "^4.3.3", - "flatted": "^3.2.5", + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", "rfdc": "^1.3.0", - "streamroller": "^3.0.4" + "streamroller": "^3.1.3" }, "engines": { "node": ">=8.0" @@ -14410,7 +16658,7 @@ "node_modules/lru-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", "dev": true, "dependencies": { "es5-ext": "~0.10.2" @@ -14476,7 +16724,7 @@ "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -14494,13 +16742,13 @@ "node_modules/map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", "dev": true }, "node_modules/map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", "dev": true, "dependencies": { "object-visit": "^1.0.0" @@ -14520,15 +16768,15 @@ } }, "node_modules/marky": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.4.tgz", - "integrity": "sha512-zd2/GiSn6U3/jeFVZ0J9CA1LzQ8RfIVvXkb/U0swFHF/zT+dVohTAWjmo2DcIuofmIIIROlwTbd+shSeXmxr0w==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", + "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", "dev": true }, "node_modules/matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", - "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "integrity": "sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==", "dev": true, "dependencies": { "findup-sync": "^2.0.0", @@ -14540,10 +16788,110 @@ "node": ">= 0.10.0" } }, + "node_modules/matchdep/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/braces/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/fill-range/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/matchdep/node_modules/findup-sync": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "integrity": "sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==", "dev": true, "dependencies": { "detect-file": "^1.0.0", @@ -14555,10 +16903,16 @@ "node": ">= 0.10" } }, + "node_modules/matchdep/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "node_modules/matchdep/node_modules/is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, "dependencies": { "is-extglob": "^2.1.0" @@ -14567,6 +16921,76 @@ "node": ">=0.10.0" } }, + "node_modules/matchdep/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/mdast-util-definitions": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.1.tgz", @@ -14708,11 +17132,12 @@ } }, "node_modules/mdast-util-gfm-table": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz", - "integrity": "sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.6.tgz", + "integrity": "sha512-uHR+fqFq3IvB3Rd4+kzXW8dmpxUhvgCQZep6KdjsLK4O6meK5dYZEayLtIxNus1XO3gfjfcIFe8a7L0HZRGgag==", "dev": true, "dependencies": { + "@types/mdast": "^3.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^1.0.0", "mdast-util-to-markdown": "^1.3.0" @@ -14739,24 +17164,22 @@ "node_modules/mdast-util-inject": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/mdast-util-inject/-/mdast-util-inject-1.1.0.tgz", - "integrity": "sha1-2wa4tYW+lZotzS+H9HK6m3VvNnU=", + "integrity": "sha512-CcJ0mHa36QYumDKiZ2OIR+ClhfOM7zIzN+Wfy8tRZ1hpH9DKLCS+Mh4DyK5bCxzE9uxMWcbIpeNFWsg1zrj/2g==", "dev": true, "dependencies": { "mdast-util-to-string": "^1.0.0" } }, "node_modules/mdast-util-to-hast": { - "version": "12.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.2.1.tgz", - "integrity": "sha512-dyindR2P7qOqXO1hQirZeGtVbiX7xlNQbw7gGaAwN4A1dh4+X8xU/JyYmRoyB8Fu1uPXzp7mlL5QwW7k+knvgA==", + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.2.4.tgz", + "integrity": "sha512-a21xoxSef1l8VhHxS1Dnyioz6grrJkoaCUgGzMD/7dWHvboYX3VW53esRUfB5tgTyz4Yos1n25SPcj35dJqmAg==", "dev": true, "dependencies": { "@types/hast": "^2.0.0", "@types/mdast": "^3.0.0", - "@types/mdurl": "^1.0.0", "mdast-util-definitions": "^5.0.0", - "mdurl": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-sanitize-uri": "^1.1.0", "trim-lines": "^3.0.0", "unist-builder": "^3.0.0", "unist-util-generated": "^2.0.0", @@ -14866,16 +17289,10 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.6" } @@ -14912,7 +17329,7 @@ "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -14923,15 +17340,15 @@ "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { "node": ">= 0.6" } }, "node_modules/micromark": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", - "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.1.0.tgz", + "integrity": "sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==", "dev": true, "funding": [ { @@ -15419,9 +17836,9 @@ } }, "node_modules/micromark-util-sanitize-uri": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz", - "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.1.0.tgz", + "integrity": "sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==", "dev": true, "funding": [ { @@ -15494,118 +17911,16 @@ ] }, "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, "node_modules/mime": { @@ -15621,19 +17936,19 @@ } }, "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -15679,10 +17994,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mixin-deep": { "version": "1.3.2", @@ -15755,6 +18073,15 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/mocha/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -15776,15 +18103,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -15813,6 +18131,17 @@ "node": ">=8" } }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/mocha/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -15831,29 +18160,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/mocha/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, - "dependencies": { - "ms": "2.1.2" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.3.1" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -15882,6 +18197,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/mocha/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -15937,24 +18293,21 @@ "node": ">=10" } }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/mocha/node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/mocha/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -15985,15 +18338,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/mocha/node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -16006,11 +18350,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", @@ -16064,21 +18417,24 @@ "ms": "2.0.0" } }, - "node_modules/morgan/node_modules/depd": { + "node_modules/morgan/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, "engines": { "node": ">= 0.8" } }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "node_modules/mpd-parser": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.21.1.tgz", @@ -16104,9 +18460,9 @@ } }, "node_modules/mrmime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.0.tgz", - "integrity": "sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", "dev": true, "engines": { "node": ">=10" @@ -16120,45 +18476,12 @@ "node_modules/multipipe": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "integrity": "sha512-7ZxrUybYv9NonoXgwoOqtStIu18D1c3eFZj27hqgf5kBrBF8Q+tE8V0MW8dKM5QLkQPh1JhhbKgHLY9kifov4Q==", "dev": true, "dependencies": { "duplexer2": "0.0.2" } }, - "node_modules/multipipe/node_modules/duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", - "dev": true, - "dependencies": { - "readable-stream": "~1.1.9" - } - }, - "node_modules/multipipe/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "node_modules/multipipe/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/multipipe/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, "node_modules/mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", @@ -16199,11 +18522,10 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true, - "optional": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -16233,6 +18555,28 @@ "node": ">=0.10.0" } }, + "node_modules/nanomatch/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/nanomatch/node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -16245,13 +18589,13 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/ncp": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", - "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", + "integrity": "sha512-PfGU8jYWdRl4FqJfCy0IzbkGyFHntfWygZg46nFk/dJD/XRrk2cj0SsKSX9n5u5gE0E0YfEpKWrEkfjnlZSTXA==", "dev": true, "bin": { "ncp": "bin/ncp" @@ -16309,7 +18653,7 @@ "node_modules/nise/node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "node_modules/nise/node_modules/lolex": { @@ -16358,7 +18702,7 @@ "node_modules/nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", "dev": true, "dependencies": { "abbrev": "1" @@ -16368,24 +18712,33 @@ } }, "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/normalize-path": { @@ -16424,7 +18777,7 @@ "node_modules/npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "dependencies": { "path-key": "^2.0.0" @@ -16436,7 +18789,7 @@ "node_modules/npm-run-path/node_modules/path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "engines": { "node": ">=4" @@ -16445,7 +18798,7 @@ "node_modules/number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -16463,7 +18816,7 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -16472,7 +18825,7 @@ "node_modules/object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", "dev": true, "dependencies": { "copy-descriptor": "^0.1.0", @@ -16486,7 +18839,7 @@ "node_modules/object-copy/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "dependencies": { "is-descriptor": "^0.1.0" @@ -16498,7 +18851,7 @@ "node_modules/object-copy/node_modules/is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -16516,7 +18869,7 @@ "node_modules/object-copy/node_modules/is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -16551,7 +18904,7 @@ "node_modules/object-copy/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -16561,10 +18914,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true, + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -16589,6 +18941,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "engines": { "node": ">= 0.4" } @@ -16596,7 +18949,7 @@ "node_modules/object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", "dev": true, "dependencies": { "isobject": "^3.0.0" @@ -16606,13 +18959,14 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -16625,7 +18979,7 @@ "node_modules/object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", "dev": true, "dependencies": { "array-each": "^1.0.1", @@ -16640,7 +18994,7 @@ "node_modules/object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", "dev": true, "dependencies": { "for-own": "^1.0.0", @@ -16653,7 +19007,7 @@ "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "dev": true, "dependencies": { "isobject": "^3.0.1" @@ -16665,7 +19019,7 @@ "node_modules/object.reduce": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", - "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "integrity": "sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==", "dev": true, "dependencies": { "for-own": "^1.0.0", @@ -16693,9 +19047,9 @@ } }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, @@ -16715,7 +19069,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" @@ -16760,7 +19114,7 @@ "node_modules/opn/node_modules/is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", "dev": true, "engines": { "node": ">=4" @@ -16855,6 +19209,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ora/node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -16886,7 +19249,7 @@ "node_modules/ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==", "dev": true, "dependencies": { "readable-stream": "^2.0.1" @@ -16895,7 +19258,7 @@ "node_modules/os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -16904,7 +19267,7 @@ "node_modules/os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", "dev": true, "dependencies": { "lcid": "^1.0.0" @@ -16916,7 +19279,7 @@ "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, "engines": { "node": ">=0.10.0" @@ -16934,7 +19297,7 @@ "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, "engines": { "node": ">=4" @@ -16950,36 +19313,39 @@ } }, "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "p-limit": "^1.1.0" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/parent-module": { @@ -16997,7 +19363,7 @@ "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, "dependencies": { "is-absolute": "^1.0.0", @@ -17047,7 +19413,7 @@ "node_modules/parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -17085,7 +19451,7 @@ "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -17094,22 +19460,22 @@ "node_modules/path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", "dev": true }, "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -17132,7 +19498,7 @@ "node_modules/path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, "dependencies": { "path-root-regex": "^0.1.0" @@ -17144,7 +19510,7 @@ "node_modules/path-root-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -17153,7 +19519,7 @@ "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "node_modules/path-type": { "version": "1.1.0", @@ -17190,7 +19556,7 @@ "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", "dev": true, "dependencies": { "through": "~2.3" @@ -17199,13 +19565,13 @@ "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, "node_modules/picocolors": { @@ -17240,7 +19606,7 @@ "node_modules/pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -17249,7 +19615,7 @@ "node_modules/pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "dependencies": { "pinkie": "^2.0.0" @@ -17282,98 +19648,17 @@ "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dev": true, - "dependencies": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/plugin-error/node_modules/ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", "dev": true, "dependencies": { - "ansi-wrap": "^0.1.0" + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" }, "engines": { "node": ">=0.10.0" @@ -17382,16 +19667,16 @@ "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "version": "8.4.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", + "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", "dev": true, "funding": [ { @@ -17413,6 +19698,19 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "optional": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -17423,35 +19721,57 @@ } }, "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", "react-is": "^17.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10" } }, "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/pretty-format/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/pretty-format/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", "dev": true, "engines": { "node": ">= 0.8" @@ -17472,18 +19792,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/printj": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz", - "integrity": "sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg==", - "dev": true, - "bin": { - "printj": "bin/printj.njs" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -17554,7 +19862,7 @@ "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true }, "node_modules/ps-tree": { @@ -17575,13 +19883,13 @@ "node_modules/pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "node_modules/pump": { @@ -17637,16 +19945,16 @@ } }, "node_modules/puppeteer-core": { - "version": "13.5.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.5.1.tgz", - "integrity": "sha512-dobVqWjV34ilyfQHR3BBnCYaekBYTi5MgegEYBRYd3s3uFy8jUpZEEWbaFjG9ETm+LGzR5Lmr0aF6LLuHtiuCg==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.7.0.tgz", + "integrity": "sha512-rXja4vcnAzFAP1OVLq/5dWNfwBGuzcOARJ6qGV7oAZhnLmVRU8G5MsdeQEAOy332ZhkIOnn9jp15R89LKHyp2Q==", "dev": true, "dependencies": { "cross-fetch": "3.1.5", - "debug": "4.3.3", - "devtools-protocol": "0.0.969999", + "debug": "4.3.4", + "devtools-protocol": "0.0.981744", "extract-zip": "2.0.1", - "https-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", "pkg-dir": "4.2.0", "progress": "2.0.3", "proxy-from-env": "1.1.0", @@ -17659,37 +19967,12 @@ "node": ">=10.18.1" } }, - "node_modules/puppeteer-core/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.969999", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.969999.tgz", - "integrity": "sha512-6GfzuDWU0OFAuOvBokXpXPLxjOJ5DZ157Ue3sGQQM3LgAamb8m0R0ruSfN0DDu+XG5XJgT50i6zZ/0o8RglreQ==", + "version": "0.0.981744", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.981744.tgz", + "integrity": "sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==", "dev": true }, - "node_modules/puppeteer-core/node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/puppeteer-core/node_modules/ws": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", @@ -17714,7 +19997,7 @@ "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", "dev": true, "engines": { "node": ">=0.6.0", @@ -17731,9 +20014,12 @@ } }, "node_modules/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" }, @@ -17750,7 +20036,7 @@ "node_modules/querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", "dev": true, "engines": { @@ -17793,12 +20079,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { "bytes": "3.1.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -17941,48 +20227,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/read-pkg/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -18013,16 +20257,43 @@ "node_modules/readable-stream/node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "node_modules/readdir-glob": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz", - "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz", + "integrity": "sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==", "dev": true, "dependencies": { - "minimatch": "^3.0.4" + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, "node_modules/readdirp": { @@ -18040,7 +20311,7 @@ "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, "dependencies": { "resolve": "^1.1.6" @@ -18050,27 +20321,15 @@ } }, "node_modules/recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "dev": true, "dependencies": { - "minimatch": "3.0.4" + "minimatch": "^3.0.5" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/recursive-readdir/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": ">=6.0.0" } }, "node_modules/regenerate": { @@ -18079,9 +20338,9 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dependencies": { "regenerate": "^1.4.2" }, @@ -18090,15 +20349,14 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" }, "node_modules/regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", "dependencies": { "@babel/runtime": "^7.8.4" } @@ -18116,14 +20374,28 @@ "node": ">=0.10.0" } }, + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -18145,14 +20417,14 @@ } }, "node_modules/regexpu-core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", - "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", + "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.0.0" }, @@ -18161,14 +20433,14 @@ } }, "node_modules/regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" }, "node_modules/regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dependencies": { "jsesc": "~0.5.0" }, @@ -18179,7 +20451,7 @@ "node_modules/regjsparser/node_modules/jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "bin": { "jsesc": "bin/jsesc" } @@ -18315,7 +20587,7 @@ "node_modules/remove-bom-stream": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", "dev": true, "dependencies": { "remove-bom-buffer": "^3.0.0", @@ -18339,7 +20611,7 @@ "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true }, "node_modules/repeat-element": { @@ -18354,7 +20626,7 @@ "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true, "engines": { "node": ">=0.10" @@ -18363,7 +20635,7 @@ "node_modules/repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", "dev": true, "dependencies": { "is-finite": "^1.0.0" @@ -18375,7 +20647,7 @@ "node_modules/replace-ext": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", "dev": true, "engines": { "node": ">= 0.4" @@ -18384,7 +20656,7 @@ "node_modules/replace-homedir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", - "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "integrity": "sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg==", "dev": true, "dependencies": { "homedir-polyfill": "^1.0.1", @@ -18450,7 +20722,7 @@ "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -18465,10 +20737,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "node_modules/resolve": { @@ -18496,7 +20774,7 @@ "node_modules/resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, "dependencies": { "expand-tilde": "^2.0.0", @@ -18518,7 +20796,7 @@ "node_modules/resolve-options": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", "dev": true, "dependencies": { "value-or-function": "^3.0.0" @@ -18530,17 +20808,20 @@ "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", "deprecated": "https://github.com/lydell/resolve-url#deprecated", "dev": true }, "node_modules/responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "dependencies": { "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/resq": { @@ -18555,7 +20836,7 @@ "node_modules/resq/node_modules/fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", "dev": true }, "node_modules/restore-cursor": { @@ -18626,20 +20907,17 @@ } }, "node_modules/rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "dependencies": { - "tslib": "^2.1.0" + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -18653,25 +20931,53 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safe-json-parse": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", + "integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A==", "dev": true }, "node_modules/safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", "dev": true, "dependencies": { "ret": "~0.1.10" } }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -18718,15 +21024,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -18738,7 +21035,7 @@ "node_modules/semver-greatest-satisfied-range": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", - "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "integrity": "sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ==", "dev": true, "dependencies": { "sver-compat": "^1.5.0" @@ -18748,23 +21045,23 @@ } }, "node_modules/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" @@ -18781,7 +21078,7 @@ "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/send/node_modules/mime": { "version": "1.6.0", @@ -18838,7 +21135,7 @@ "node_modules/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, "dependencies": { "accepts": "~1.3.4", @@ -18862,10 +21159,19 @@ "ms": "2.0.0" } }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-index/node_modules/http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "dependencies": { "depd": "~1.1.2", @@ -18880,13 +21186,13 @@ "node_modules/serve-index/node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, "node_modules/serve-index/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "node_modules/serve-index/node_modules/setprototypeof": { @@ -18895,15 +21201,24 @@ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" @@ -18912,7 +21227,7 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "node_modules/set-value": { @@ -18933,7 +21248,7 @@ "node_modules/set-value/node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "dependencies": { "is-extendable": "^0.1.0" @@ -18945,7 +21260,7 @@ "node_modules/set-value/node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -18966,7 +21281,7 @@ "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true }, "node_modules/setprototypeof": { @@ -18999,7 +21314,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -19040,27 +21354,6 @@ "node": ">=0.3.1" } }, - "node_modules/sinon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/sinon/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/sirv": { "version": "1.0.19", "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", @@ -19076,12 +21369,12 @@ } }, "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/slice-ansi": { @@ -19170,7 +21463,7 @@ "node_modules/snapdragon-node/node_modules/define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, "dependencies": { "is-descriptor": "^1.0.0" @@ -19200,7 +21493,7 @@ "node_modules/snapdragon-util/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -19221,7 +21514,7 @@ "node_modules/snapdragon/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "dependencies": { "is-descriptor": "^0.1.0" @@ -19233,7 +21526,7 @@ "node_modules/snapdragon/node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "dependencies": { "is-extendable": "^0.1.0" @@ -19245,7 +21538,7 @@ "node_modules/snapdragon/node_modules/is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -19257,7 +21550,7 @@ "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -19275,7 +21568,7 @@ "node_modules/snapdragon/node_modules/is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -19287,7 +21580,7 @@ "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -19313,7 +21606,7 @@ "node_modules/snapdragon/node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -19322,7 +21615,7 @@ "node_modules/snapdragon/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "node_modules/snapdragon/node_modules/source-map-resolve": { @@ -19340,36 +21633,35 @@ } }, "node_modules/socket.io": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", - "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", + "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.0" }, "engines": { "node": ">=10.0.0" } }, "node_modules/socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", "dev": true }, "node_modules/socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", "dev": true, "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" }, "engines": { @@ -19385,7 +21677,7 @@ "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -19413,22 +21705,12 @@ } }, "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "source-map": "^0.5.6" } }, "node_modules/source-map-url": { @@ -19491,15 +21773,15 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, "node_modules/split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", "dev": true, "dependencies": { "through": "2" @@ -19520,19 +21802,46 @@ "node": ">=0.10.0" } }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/split2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", - "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/split2/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, "engines": { - "node": ">= 10.x" + "node": ">= 6" } }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "node_modules/sshpk": { @@ -19563,7 +21872,7 @@ "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "dev": true, "engines": { "node": "*" @@ -19593,7 +21902,7 @@ "node_modules/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", "dev": true, "dependencies": { "define-property": "^0.2.5", @@ -19606,7 +21915,7 @@ "node_modules/static-extend/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "dependencies": { "is-descriptor": "^0.1.0" @@ -19618,7 +21927,7 @@ "node_modules/static-extend/node_modules/is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -19630,7 +21939,7 @@ "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -19648,7 +21957,7 @@ "node_modules/static-extend/node_modules/is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -19660,7 +21969,7 @@ "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -19684,11 +21993,11 @@ } }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/stream-buffers": { @@ -19703,7 +22012,7 @@ "node_modules/stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", "dev": true, "dependencies": { "duplexer": "~0.1.1" @@ -19722,19 +22031,51 @@ "dev": true }, "node_modules/streamroller": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.4.tgz", - "integrity": "sha512-GI9NzeD+D88UFuIlJkKNDH/IsuR+qIN7Qh8EsmhoRZr9bQoehTraRgwtLUkZbpcAw+hLPfHOypmppz8YyGK68w==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.3.tgz", + "integrity": "sha512-CphIJyFx2SALGHeINanjFRKQ4l7x2c+rXYJ4BMq0gd+ZK0gi4VT8b+eHe2wi58x4UayBAKx4xtHpXT/ea1cz8w==", "dev": true, "dependencies": { - "date-format": "^4.0.4", - "debug": "^4.3.3", - "fs-extra": "^10.0.1" + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" }, "engines": { "node": ">=8.0" } }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/streamroller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -19744,10 +22085,16 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/string-template": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", + "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==", "dev": true }, "node_modules/string-width": { @@ -19764,33 +22111,29 @@ "node": ">=8" } }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -19825,7 +22168,7 @@ "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { "node": ">=4" @@ -19834,7 +22177,7 @@ "node_modules/strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", "dev": true, "engines": { "node": ">=0.10.0" @@ -19843,34 +22186,42 @@ "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/strip-json-comments": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.0.tgz", + "integrity": "sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/suffix": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/suffix/-/suffix-0.1.1.tgz", - "integrity": "sha1-zFgjFkag7xEC95R47zqSSP2chC8=", + "integrity": "sha512-j5uf6MJtMCfC4vBe5LFktSe4bGyNTBk7I2Kdri0jeLrcv5B9pWfxVa5JQpoxgtR8vaVB7bVxsWgnfQbX5wkhAA==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=4" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -19887,7 +22238,7 @@ "node_modules/sver-compat": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", - "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "integrity": "sha512-aFTHfmjwizMNlNE6dsGmoAM4lHjL0CyiobWaFiXWSlD7cIxshW422Nb8KbXCmR6z+0ZEPY+daXJrDyh/vuwTyg==", "dev": true, "dependencies": { "es6-iterator": "^2.0.1", @@ -19911,9 +22262,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", - "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -19986,7 +22337,7 @@ "node_modules/temp-fs": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", - "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", + "integrity": "sha512-WfecDCR1xC9b0nsrzSaxPf3ZuWeWLUWblW4vlDQAa1biQaKHiImHnJfeQocQe/hXKMcolRzgkcVX/7kK4zoWbw==", "dev": true, "dependencies": { "rimraf": "~2.5.2" @@ -19998,7 +22349,7 @@ "node_modules/temp-fs/node_modules/rimraf": { "version": "2.5.4", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "integrity": "sha512-Lw7SHMjssciQb/rRz7JyPIy9+bbUshEucPoLRvWqy09vC5zQixl8Uet+Zl+SROBB/JMWHJRdCk1qdxNWHNMvlQ==", "dev": true, "dependencies": { "glob": "^7.0.5" @@ -20030,9 +22381,9 @@ } }, "node_modules/terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", + "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.2", @@ -20048,16 +22399,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "dependencies": { + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "engines": { "node": ">= 10.13.0" @@ -20097,15 +22448,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -20124,7 +22466,19 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { + "node_modules/terser/node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/terser/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", @@ -20133,24 +22487,16 @@ "node": ">=0.10.0" } }, - "node_modules/terser/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -20168,7 +22514,7 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "node_modules/textextensions": { @@ -20186,7 +22532,7 @@ "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "node_modules/through2": { @@ -20235,7 +22581,7 @@ "node_modules/time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -20280,21 +22626,21 @@ } }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "dependencies": { - "rimraf": "^3.0.0" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": ">=8.17.0" + "node": ">=0.6.0" } }, "node_modules/to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", "dev": true, "dependencies": { "is-absolute": "^1.0.0", @@ -20307,7 +22653,7 @@ "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "engines": { "node": ">=4" } @@ -20315,7 +22661,7 @@ "node_modules/to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", "dev": true, "dependencies": { "kind-of": "^3.0.2" @@ -20333,7 +22679,7 @@ "node_modules/to-object-path/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { "is-buffer": "^1.1.5" @@ -20369,19 +22715,23 @@ "node": ">=8.0" } }, - "node_modules/to-regex-range/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, "engines": { - "node": ">=0.12.0" + "node": ">=0.10.0" } }, "node_modules/to-through": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", - "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", "dev": true, "dependencies": { "through2": "^2.0.3" @@ -20433,13 +22783,13 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, "node_modules/traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", "dev": true, "engines": { "node": "*" @@ -20458,7 +22808,7 @@ "node_modules/trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -20475,14 +22825,14 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", - "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, @@ -20507,7 +22857,7 @@ "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "dependencies": { "safe-buffer": "^5.0.1" @@ -20519,7 +22869,7 @@ "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "node_modules/type": { @@ -20576,7 +22926,7 @@ "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, "node_modules/typescript": { @@ -20615,9 +22965,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "version": "0.7.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", + "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==", "dev": true, "funding": [ { @@ -20634,9 +22984,9 @@ } }, "node_modules/uglify-js": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.2.tgz", - "integrity": "sha512-peeoTk3hSwYdoc9nrdiEJk+gx1ALCtTjdYuKSXMTDqq7n1W7dHPqWDdSi+BPL0ni2YMeHD7hKUSdbj3TZauY2A==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true, "bin": { @@ -20647,14 +22997,14 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -20674,7 +23024,7 @@ "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -20704,7 +23054,7 @@ "node_modules/undertaker-registry": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", + "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", "dev": true, "engines": { "node": ">= 0.10" @@ -20713,7 +23063,7 @@ "node_modules/undertaker/node_modules/fast-levenshtein": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", - "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", + "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -20745,9 +23095,9 @@ } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "engines": { "node": ">=4" } @@ -20786,10 +23136,19 @@ "node": ">=0.10.0" } }, + "node_modules/union-value/node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/union-value/node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -20905,7 +23264,7 @@ "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "engines": { "node": ">= 0.8" } @@ -20913,7 +23272,7 @@ "node_modules/unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", "dev": true, "dependencies": { "has-value": "^0.3.1", @@ -20926,7 +23285,7 @@ "node_modules/unset-value/node_modules/has-value": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", "dev": true, "dependencies": { "get-value": "^2.0.3", @@ -20940,7 +23299,7 @@ "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", "dev": true, "dependencies": { "isarray": "1.0.0" @@ -20952,7 +23311,7 @@ "node_modules/unset-value/node_modules/has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -20961,7 +23320,7 @@ "node_modules/unset-value/node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "node_modules/unzipper": { @@ -20981,6 +23340,15 @@ "setimmediate": "~1.0.4" } }, + "node_modules/unzipper/node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -20992,9 +23360,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.7.tgz", - "integrity": "sha512-iN/XYesmZ2RmmWAiI4Z5rq0YqSiv0brj9Ce9CfhNE4xIW2h+MFxcgkxIzZ+ShkFPUkjU3gQ+3oypadD3RAMtrg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "funding": [ { "type": "opencollective", @@ -21028,14 +23396,14 @@ "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, "node_modules/url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", "dev": true, "dependencies": { "punycode": "1.3.2", @@ -21061,7 +23429,7 @@ "node_modules/url/node_modules/punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", "dev": true }, "node_modules/use": { @@ -21074,29 +23442,28 @@ } }, "node_modules/util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dev": true, "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", "which-typed-array": "^1.1.2" } }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "engines": { "node": ">= 0.4.0" } @@ -21160,7 +23527,7 @@ "node_modules/value-or-function": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", - "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", "dev": true, "engines": { "node": ">= 0.10" @@ -21169,7 +23536,7 @@ "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "engines": { "node": ">= 0.8" } @@ -21177,7 +23544,7 @@ "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "engines": [ "node >=0.6.0" @@ -21191,13 +23558,13 @@ "node_modules/verror/node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, "node_modules/vfile": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.4.tgz", - "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.5.tgz", + "integrity": "sha512-U1ho2ga33eZ8y8pkbQLH54uKqGhFJ6GYIHnnG5AhRpAh3OWjkrRHKa/KogbmQn8We+c0KVV3rTOgR9V/WowbXQ==", "dev": true, "dependencies": { "@types/unist": "^2.0.0", @@ -21254,6 +23621,12 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/vfile-reporter/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, "node_modules/vfile-reporter/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -21325,13 +23698,13 @@ } }, "node_modules/video.js": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.20.2.tgz", - "integrity": "sha512-hdvAHKAyaL6bCDkeu0pPtFYKi1EDaOUovm7FN1xqBDolUxgH8FKy1WIgTS+Ouuaw7R54SCTcSeXjZEizhy9ouQ==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.20.3.tgz", + "integrity": "sha512-JMspxaK74LdfWcv69XWhX4rILywz/eInOVPdKefpQiZJSMD5O8xXYueqACP2Q5yqKstycgmmEKlJzZ+kVmDciw==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", - "@videojs/http-streaming": "2.14.2", + "@videojs/http-streaming": "2.14.3", "@videojs/vhs-utils": "^3.0.4", "@videojs/xhr": "2.6.0", "aes-decrypter": "3.1.3", @@ -21342,7 +23715,7 @@ "mux.js": "6.0.1", "safe-json-parse": "4.0.0", "videojs-font": "3.2.0", - "videojs-vtt.js": "^0.15.3" + "videojs-vtt.js": "^0.15.4" } }, "node_modules/video.js/node_modules/safe-json-parse": { @@ -21474,7 +23847,7 @@ "node_modules/vinyl-sourcemap": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", - "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", "dev": true, "dependencies": { "append-buffer": "^1.0.2", @@ -21492,7 +23865,7 @@ "node_modules/vinyl-sourcemap/node_modules/normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, "dependencies": { "remove-trailing-separator": "^1.0.1" @@ -21504,7 +23877,7 @@ "node_modules/vinyl-sourcemaps-apply": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", "dev": true, "dependencies": { "source-map": "^0.5.1" @@ -21522,16 +23895,16 @@ "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/vue-template-compiler": { - "version": "2.7.10", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.10.tgz", - "integrity": "sha512-QO+8R9YRq1Gudm8ZMdo/lImZLJVUIAM8c07Vp84ojdDAf8HmPJc7XB556PcXV218k2AkKznsRz6xB5uOjAC4EQ==", + "version": "2.7.13", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.13.tgz", + "integrity": "sha512-jYM6TClwDS9YqP48gYrtAtaOhRKkbYmbzE+Q51gX5YDr777n7tNI/IZk4QV4l/PjQPNh/FVa/E92sh/RqKMrog==", "dev": true, "optional": true, "dependencies": { @@ -21549,9 +23922,9 @@ } }, "node_modules/watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -21564,54 +23937,148 @@ "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, "dependencies": { "defaults": "^1.0.3" } }, "node_modules/webdriver": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.16.16.tgz", - "integrity": "sha512-x8UoG9k/P8KDrfSh1pOyNevt9tns3zexoMxp9cKnyA/7HYSErhZYTLGlgxscAXLtQG41cMH/Ba/oBmOx7Hgd8w==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.5.3.tgz", + "integrity": "sha512-cDTn/hYj5x8BYwXxVb/WUwqGxrhCMP2rC8ttIWCfzmiVtmOnJGulC7CyxU3+p9Q5R/gIKTzdJOss16dhb+5CoA==", "dev": true, "dependencies": { - "@types/node": "^17.0.4", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", "got": "^11.0.2", - "ky": "^0.29.0", "lodash.merge": "^4.6.1" }, "engines": { "node": ">=12.0.0" } }, + "node_modules/webdriver/node_modules/@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriver/node_modules/@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "dependencies": { + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriver/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/webdriver/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/webdriver/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/webdriver/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/webdriver/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webdriver/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/webdriverio": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.16.16.tgz", - "integrity": "sha512-caPaEWyuD3Qoa7YkW4xCCQA4v9Pa9wmhFGPvNZh3ERtjMCNi8L/XXOdkekWNZmFh3tY0kFguBj7+fAwSY7HAGw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.25.4.tgz", + "integrity": "sha512-agkgwn2SIk5cAJ03uue1GnGZcUZUDN3W4fUMY9/VfO8bVJrPEgWg31bPguEWPu+YhEB/aBJD8ECxJ3OEKdy4qQ==", "dev": true, "dependencies": { "@types/aria-query": "^5.0.0", - "@types/node": "^17.0.4", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/repl": "7.16.14", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@types/node": "^18.0.0", + "@wdio/config": "7.25.4", + "@wdio/logger": "7.19.0", + "@wdio/protocols": "7.22.0", + "@wdio/repl": "7.25.4", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", "archiver": "^5.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools": "7.16.16", - "devtools-protocol": "^0.0.973690", + "devtools": "7.25.4", + "devtools-protocol": "^0.0.1061995", "fs-extra": "^10.0.0", - "get-port": "^5.1.1", "grapheme-splitter": "^1.0.2", "lodash.clonedeep": "^4.5.0", "lodash.isobject": "^3.0.2", @@ -21623,12 +24090,120 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^8.0.0", - "webdriver": "7.16.16" + "webdriver": "7.25.4" }, "engines": { "node": ">=12.0.0" } }, + "node_modules/webdriverio/node_modules/@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "dev": true + }, + "node_modules/webdriverio/node_modules/@wdio/config": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.25.4.tgz", + "integrity": "sha512-vb0emDtD9FbFh/yqW6oNdo2iuhQp8XKj6GX9fyy9v4wZgg3B0HPMVJxhIfcoHz7LMBWlHSo9YdvhFI5EQHRLBA==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.19.0", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", + "deepmerge": "^4.0.0", + "glob": "^8.0.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriverio/node_modules/@wdio/logger": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.19.0.tgz", + "integrity": "sha512-xR7SN/kGei1QJD1aagzxs3KMuzNxdT/7LYYx+lt6BII49+fqL/SO+5X0FDCZD0Ds93AuQvvz9eGyzrBI2FFXmQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriverio/node_modules/@wdio/protocols": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.22.0.tgz", + "integrity": "sha512-8EXRR+Ymdwousm/VGtW3H1hwxZ/1g1H99A1lF0U4GuJ5cFWHCd0IVE5H31Z52i8ZruouW8jueMkGZPSo2IIUSQ==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriverio/node_modules/@wdio/repl": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.25.4.tgz", + "integrity": "sha512-kYhj9gLsUk4HmlXLqkVre+gwbfvw9CcnrHjqIjrmMS4mR9D8zvBb5CItb3ZExfPf9jpFzIFREbCAYoE9x/kMwg==", + "dev": true, + "dependencies": { + "@wdio/utils": "7.25.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriverio/node_modules/@wdio/types": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.25.4.tgz", + "integrity": "sha512-muvNmq48QZCvocctnbe0URq2FjJjUPIG4iLoeMmyF0AQgdbjaUkMkw3BHYNHVTbSOU9WMsr2z8alhj/I2H6NRQ==", + "dev": true, + "dependencies": { + "@types/node": "^18.0.0", + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "^4.6.2" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/webdriverio/node_modules/@wdio/utils": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.25.4.tgz", + "integrity": "sha512-8iwQDk+foUqSzKZKfhLxjlCKOkfRJPNHaezQoevNgnrTq/t0ek+ldZCATezb9B8jprAuP4mgS9xi22akc6RkzA==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.19.0", + "@wdio/types": "7.25.4", + "p-iteration": "^1.1.8" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriverio/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/webdriverio/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -21638,6 +24213,80 @@ "balanced-match": "^1.0.0" } }, + "node_modules/webdriverio/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/webdriverio/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/webdriverio/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/webdriverio/node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webdriverio/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webdriverio/node_modules/ky": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.30.0.tgz", + "integrity": "sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, "node_modules/webdriverio/node_modules/minimatch": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", @@ -21650,16 +24299,48 @@ "node": ">=10" } }, + "node_modules/webdriverio/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webdriverio/node_modules/webdriver": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.25.4.tgz", + "integrity": "sha512-6nVDwenh0bxbtUkHASz9B8T9mB531Fn1PcQjUGj2t5dolLPn6zuK1D7XYVX40hpn6r3SlYzcZnEBs4X0az5Txg==", + "dev": true, + "dependencies": { + "@types/node": "^18.0.0", + "@wdio/config": "7.25.4", + "@wdio/logger": "7.19.0", + "@wdio/protocols": "7.22.0", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", + "got": "^11.0.2", + "ky": "0.30.0", + "lodash.merge": "^4.6.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, "node_modules/webpack": { - "version": "5.70.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz", - "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==", + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -21667,24 +24348,24 @@ "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", + "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.9.2", + "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "bin": { @@ -21704,9 +24385,9 @@ } }, "node_modules/webpack-bundle-analyzer": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz", - "integrity": "sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz", + "integrity": "sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg==", "dev": true, "dependencies": { "acorn": "^8.0.4", @@ -21727,9 +24408,9 @@ } }, "node_modules/webpack-bundle-analyzer/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -21738,15 +24419,6 @@ "node": ">=0.4.0" } }, - "node_modules/webpack-bundle-analyzer/node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/webpack-bundle-analyzer/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -21805,6 +24477,15 @@ "node": ">= 10" } }, + "node_modules/webpack-bundle-analyzer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/webpack-bundle-analyzer/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -21818,9 +24499,9 @@ } }, "node_modules/webpack-bundle-analyzer/node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "dev": true, "engines": { "node": ">=8.3.0" @@ -21916,10 +24597,92 @@ "webpack": "^5.21.2" } }, + "node_modules/webpack-stream/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-stream/node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/webpack-stream/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/webpack/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -21953,15 +24716,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -22006,7 +24760,7 @@ "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "dependencies": { "tr46": "~0.0.3", @@ -22059,18 +24813,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true + }, "node_modules/which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "is-typed-array": "^1.1.9" }, "engines": { "node": ">= 0.4" @@ -22079,6 +24839,58 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -22091,13 +24903,13 @@ "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "node_modules/wrap-ansi": { @@ -22153,7 +24965,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/write": { @@ -22228,13 +25040,13 @@ "node_modules/yargs": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.3.tgz", - "integrity": "sha1-BU3oth8i7v23IHBZ6u+da4P7kxo=", + "integrity": "sha512-7OGt4xXoWJQh5ulgZ78rKaqY7dNWbjfK+UKxGcIlaM2j7C4fqGchyv8CPvEWdRPrHp6Ula/YU8yGRpYGOHrI+g==", "dev": true }, "node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { "node": ">=12" @@ -22267,18 +25079,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yargs-unparser/node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -22291,7 +25091,7 @@ "node_modules/yarn-install": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/yarn-install/-/yarn-install-1.0.0.tgz", - "integrity": "sha1-V/RQULgu/VcYKzlzxUqgXLXSUjA=", + "integrity": "sha512-VO1u181msinhPcGvQTVMnHVOae8zjX/NSksR17e6eXHRveDvHCF5mGjh9hkN8mzyfnCqcBe42LdTs7bScuTaeg==", "dev": true, "dependencies": { "cac": "^3.0.3", @@ -22309,7 +25109,7 @@ "node_modules/yarn-install/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -22318,7 +25118,7 @@ "node_modules/yarn-install/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -22327,7 +25127,7 @@ "node_modules/yarn-install/node_modules/chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "dependencies": { "ansi-styles": "^2.2.1", @@ -22343,7 +25143,7 @@ "node_modules/yarn-install/node_modules/cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", "dev": true, "dependencies": { "lru-cache": "^4.0.1", @@ -22363,7 +25163,7 @@ "node_modules/yarn-install/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -22375,7 +25175,7 @@ "node_modules/yarn-install/node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "engines": { "node": ">=0.8.0" @@ -22396,13 +25196,13 @@ "node_modules/yarn-install/node_modules/yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "dependencies": { "buffer-crc32": "~0.2.3", @@ -22460,20 +25260,19 @@ } }, "plugins/eslint": { - "name": "eslint-plugin-prebid", "version": "1.0.0", - "integrity": "sha512-DH8lk8H4D+XCWtPm7C27IzzD4lkBHv49YTqOHeS/1mYCp4rn1j3NNNA1gl2q+CNYaAPSZDaS5DwRL/ErDP6O4Q==", "dev": true, "license": "Apache-2.0" } }, "dependencies": { "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "requires": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { @@ -22485,25 +25284,25 @@ } }, "@babel/compat-data": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.0.tgz", - "integrity": "sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw==" + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==" }, "@babel/core": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.0.tgz", - "integrity": "sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", + "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", - "@babel/helper-compilation-targets": "^7.19.0", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.0", + "@babel/generator": "^7.19.6", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helpers": "^7.19.4", + "@babel/parser": "^7.19.6", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -22512,86 +25311,96 @@ } }, "@babel/eslint-parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.17.0.tgz", - "integrity": "sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", + "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", "dev": true, "requires": { - "eslint-scope": "^5.1.1", + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", "semver": "^6.3.0" } }, "@babel/generator": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", - "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.1.tgz", + "integrity": "sha512-u1dMdBUmA7Z0rBB97xh8pIhviK7oItYOkjbsCxTWMknyvbQRBwX7/gn4JXurRdirWMFh+ZtYARqkA6ydogVZpg==", "requires": { - "@babel/types": "^7.19.0", + "@babel/types": "^7.20.0", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } } }, "@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", - "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", "requires": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" } }, "@babel/helper-compilation-targets": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz", - "integrity": "sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", "requires": { - "@babel/compat-data": "^7.19.0", + "@babel/compat-data": "^7.20.0", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.17.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", - "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", - "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^5.0.1" + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" } }, "@babel/helper-define-polyfill-provider": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", - "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "requires": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2", @@ -22604,11 +25413,11 @@ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" }, "@babel/helper-explode-assignable-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", - "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" } }, "@babel/helper-function-name": { @@ -22629,11 +25438,11 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", - "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.9" } }, "@babel/helper-module-imports": { @@ -22645,69 +25454,70 @@ } }, "@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", + "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.19.4", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4" } }, "@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" } }, "@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==" + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==" }, "@babel/helper-remap-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", - "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" } }, "@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" } }, "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", + "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.19.4" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "requires": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.20.0" } }, "@babel/helper-split-export-declaration": { @@ -22719,14 +25529,14 @@ } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==" + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" }, "@babel/helper-validator-option": { "version": "7.18.6", @@ -22734,24 +25544,24 @@ "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" }, "@babel/helper-wrap-function": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", - "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", "requires": { - "@babel/helper-function-name": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/helpers": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", - "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", "requires": { "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" } }, "@babel/highlight": { @@ -22765,169 +25575,170 @@ } }, "@babel/parser": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz", - "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==" + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.1.tgz", + "integrity": "sha512-hp0AYxaZJhxULfM1zyp7Wgr+pSUKBcP3M+PHnSzWGdXOzg/kHWIgiUWARvubhUKGOEw3xqY4x+lyZ9ytBVcELw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", - "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", - "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", - "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-proposal-class-static-block": { - "version": "7.17.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", - "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.17.6", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", - "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", - "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", - "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3" } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", - "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", - "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", - "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", - "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", + "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", "requires": { - "@babel/compat-data": "^7.17.0", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" + "@babel/plugin-transform-parameters": "^7.18.8" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", - "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", - "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", - "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.10", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", - "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", - "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-syntax-async-generators": { @@ -22970,6 +25781,14 @@ "@babel/helper-plugin-utils": "^7.8.3" } }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", @@ -23043,331 +25862,332 @@ } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", - "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", - "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", - "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", - "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.0.tgz", + "integrity": "sha512-sXOohbpHZSk7GjxK9b3dKB7CfqUD5DwOH+DggKzOQ7TXYP+RCSbRykfjQmn/zq+rBjycVRtLf9pYhAaEJA786w==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-classes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", - "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", - "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-destructuring": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz", - "integrity": "sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.0.tgz", + "integrity": "sha512-1dIhvZfkDVx/zn2S1aFwlruspTt4189j7fEkH0Y0VyuDM6bQt7bD6kLcz3l4IlLG+e5OReaBz9ROAbttRtUHqA==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", - "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", - "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", - "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-for-of": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", - "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", - "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", "requires": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", - "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", - "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", - "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", - "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "requires": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", - "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", - "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-new-target": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", - "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-object-super": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", - "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" } }, "@babel/plugin-transform-parameters": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", - "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.1.tgz", + "integrity": "sha512-nDvKLrAvl+kf6BOy1UJ3MGwzzfTMgppxwiD2Jb4LO3xjYyZq30oQzDNJbCQpMdG9+j2IXHoiMrw5Cm/L6ZoxXQ==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", - "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-regenerator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", - "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", "requires": { - "regenerator-transform": "^0.14.2" + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", - "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz", - "integrity": "sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", "requires": { "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "babel-plugin-polyfill-corejs2": "^0.3.1", - "babel-plugin-polyfill-corejs3": "^0.5.2", - "babel-plugin-polyfill-regenerator": "^0.3.1", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", "semver": "^6.3.0" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", - "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", - "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", - "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", - "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", - "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", - "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", - "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/preset-env": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", - "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.4.tgz", + "integrity": "sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==", "requires": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.11", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.19.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.19.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -23377,44 +26197,44 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.19.4", + "@babel/plugin-transform-classes": "^7.19.0", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.19.4", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.0", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", + "@babel/types": "^7.19.4", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" } }, @@ -23431,18 +26251,21 @@ } }, "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - } + "regenerator-runtime": "^0.13.10" + } + }, + "@babel/runtime-corejs3": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz", + "integrity": "sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg==", + "dev": true, + "requires": { + "core-js-pure": "^3.25.1", + "regenerator-runtime": "^0.13.10" } }, "@babel/template": { @@ -23456,29 +26279,29 @@ } }, "@babel/traverse": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.0.tgz", - "integrity": "sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", + "@babel/generator": "^7.20.1", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.0", - "@babel/types": "^7.19.0", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", - "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, @@ -23518,9 +26341,9 @@ } }, "globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -23596,7 +26419,7 @@ "@gulp-sourcemaps/map-sources": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", - "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", "dev": true, "requires": { "normalize-path": "^2.0.1", @@ -23606,7 +26429,7 @@ "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, "requires": { "remove-trailing-separator": "^1.0.1" @@ -23676,57 +26499,6 @@ "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } } }, "@istanbuljs/schema": { @@ -23736,15 +26508,15 @@ "dev": true }, "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^16.0.0", + "@types/yargs": "^15.0.0", "chalk": "^4.0.0" }, "dependencies": { @@ -23782,6 +26554,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -23794,19 +26572,18 @@ } }, "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" }, "@jridgewell/set-array": { "version": "1.1.2", @@ -23821,20 +26598,42 @@ "requires": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "requires": { + "eslint-scope": "5.1.1" } }, "@polka/url": { @@ -23879,15 +26678,15 @@ } }, "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, - "@socket.io/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, "@szmarczak/http-timer": { @@ -23900,9 +26699,9 @@ } }, "@types/aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-P+dkdFu0n08PDIvw+9nT9ByQnd+Udc8DaWPb9HKfaPwCvWvQpC5XaMRx2xLWECm9x1VKNps6vEAlirjA6+uNrQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", "dev": true }, "@types/cacheable-request": { @@ -23917,12 +26716,6 @@ "@types/responselike": "*" } }, - "@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", - "dev": true - }, "@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -23957,15 +26750,15 @@ "dev": true }, "@types/ejs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.0.tgz", - "integrity": "sha512-DCg+Ka+uDQ31lJ/UtEXVlaeV3d6t81gifaVWKJy4MYVVgvJttyX/viREy+If7fz+tK/gVxTGMtyrFPnm4gjrVA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.1.tgz", + "integrity": "sha512-RQul5wEfY7BjWm0sYY86cmUN/pcXWGyVxWX93DFFJvcrxax5zKlieLwA3T77xJGwNcZW0YW6CYG70p1m8xPFmA==", "dev": true }, "@types/eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "version": "8.4.9", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.9.tgz", + "integrity": "sha512-jFCSo4wJzlHQLCpceUhUnXdrPuCNOjGFMQ8Eg6JXxlz3QaCKOb7eGi2cephQdM4XTYsNej69P9JDJ1zqNIbncQ==", "dev": true, "requires": { "@types/estree": "*", @@ -23973,9 +26766,9 @@ } }, "@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "requires": { "@types/eslint": "*", @@ -24037,13 +26830,13 @@ "dev": true }, "@types/inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-BNoMetRf3gmkpAlV5we+kxyZTle7YibdOntIZbU5pyIfMdcwy784KfeZDAcuyMznkh5OLa17RVXZOGA5LTlkgQ==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", "dev": true, "requires": { "@types/through": "*", - "rxjs": "^7.2.0" + "rxjs": "^6.4.0" } }, "@types/istanbul-lib-coverage": { @@ -24071,54 +26864,54 @@ } }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-4.2.0.tgz", + "integrity": "sha512-xoBtGl5R9jeKUhc8ZqeYaRDx04qqJ10yhhXYGmJ4Jr8qKpvMsDQQrNUvF/wUJ4klOtmJeJM+p2Xo3zp9uaC3tw==", "dev": true, "requires": { - "@types/node": "*" + "keyv": "*" } }, "@types/lodash": { - "version": "4.14.179", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz", - "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==", + "version": "4.14.187", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.187.tgz", + "integrity": "sha512-MrO/xLXCaUgZy3y96C/iOsaIqZSeupyTImKClHunL5GrmaiII2VwvWmLBu2hwa0Kp0sV19CsyjtrTc/Fx8rg/A==", "dev": true }, "@types/lodash.flattendeep": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.6.tgz", - "integrity": "sha512-uLm2MaRVlqJSGsMK0RZpP5T3KqReq+9WbYDHCUhBhp98v56hMG/Yht52bsoTSui9xz2mUvQ9NfG3LrNGDL92Ng==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.7.tgz", + "integrity": "sha512-1h6GW/AeZw/Wej6uxrqgmdTDZX1yFS39lRsXYkg+3kWvOWWrlGCI6H7lXxlUHOzxDT4QeYGmgPpQ3BX9XevzOg==", "dev": true, "requires": { "@types/lodash": "*" } }, "@types/lodash.pickby": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz", - "integrity": "sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.pickby/-/lodash.pickby-4.6.7.tgz", + "integrity": "sha512-4ebXRusuLflfscbD0PUX4eVknDHD9Yf+uMtBIvA/hrnTqeAzbuHuDjvnYriLjUrI9YrhCPVKUf4wkRSXJQ6gig==", "dev": true, "requires": { "@types/lodash": "*" } }, "@types/lodash.union": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.union/-/lodash.union-4.6.6.tgz", - "integrity": "sha512-Wu0ZEVNcyCz8eAn6TlUbYWZoGbH9E+iOHxAZbwUoCEXdUiy6qpcz5o44mMXViM4vlPLLCPlkAubEP1gokoSZaw==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.union/-/lodash.union-4.6.7.tgz", + "integrity": "sha512-6HXM6tsnHJzKgJE0gA/LhTGf/7AbjUk759WZ1MziVm+OBNAATHhdgj+a3KVE8g76GCLAnN4ZEQQG1EGgtBIABA==", "dev": true, "requires": { "@types/lodash": "*" @@ -24133,16 +26926,10 @@ "@types/unist": "*" } }, - "@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, "@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", + "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", "dev": true }, "@types/ms": { @@ -24152,9 +26939,9 @@ "dev": true }, "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", "dev": true }, "@types/normalize-package-data": { @@ -24170,18 +26957,18 @@ "dev": true }, "@types/puppeteer": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.5.tgz", - "integrity": "sha512-lxCjpDEY+DZ66+W3x5Af4oHnEmUXt0HuaRzkBGE2UZiZEp/V1d3StpLPlmNVu/ea091bdNmVPl44lu8Wy/0ZCA==", + "version": "5.4.7", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.7.tgz", + "integrity": "sha512-JdGWZZYL0vKapXF4oQTC5hLVNfOgdPrqeZ1BiQnGk5cB7HeE91EWUiTdVSdQPobRN8rIcdffjiOgCYJ/S8QrnQ==", "dev": true, "requires": { "@types/node": "*" } }, "@types/recursive-readdir": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.0.tgz", - "integrity": "sha512-HGk753KRu2N4mWduovY4BLjYq4jTOL29gV2OfGdGxHcPSWGFkC5RRIdk+VTs5XmYd7MVAD+JwKrcb5+5Y7FOCg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.1.tgz", + "integrity": "sha512-Xd+Ptc4/F2ueInqy5yK2FI5FxtwwbX2+VZpcg+9oYsFJVen8qQKGapCr+Bi5wQtHU1cTXT8s+07lo/nKPgu8Gg==", "dev": true, "requires": { "@types/node": "*" @@ -24261,9 +27048,9 @@ "dev": true }, "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", + "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -24276,9 +27063,9 @@ "dev": true }, "@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, "requires": { @@ -24292,9 +27079,9 @@ "dev": true }, "@videojs/http-streaming": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.14.2.tgz", - "integrity": "sha512-K1raSfO/pq5r8iUas3OSYni0kXOj91n8ealIpV02khghzGv9LQ6O3YUqYd/eAhJ1HIrmZWOnrYpK/P+mhUExXQ==", + "version": "2.14.3", + "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.14.3.tgz", + "integrity": "sha512-2tFwxCaNbcEZzQugWf8EERwNMyNtspfHnvxRGRABQs09W/5SqmkWFuGWfUAm4wQKlXGfdPyAJ1338ASl459xAA==", "dev": true, "requires": { "@babel/runtime": "^7.12.5", @@ -24330,14 +27117,14 @@ } }, "@vue/compiler-core": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.38.tgz", - "integrity": "sha512-/FsvnSu7Z+lkd/8KXMa4yYNUiqQrI22135gfsQYVGuh5tqEgOB0XqrUdb/KnCLa5+TmQLPwvyUnKMyCpu+SX3Q==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.41.tgz", + "integrity": "sha512-oA4mH6SA78DT+96/nsi4p9DX97PHcNROxs51lYk7gb9Z4BPKQ3Mh+BLn6CQZBw857Iuhu28BfMSRHAlPvD4vlw==", "dev": true, "optional": true, "requires": { "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.38", + "@vue/shared": "3.2.41", "estree-walker": "^2.0.2", "source-map": "^0.6.1" }, @@ -24352,29 +27139,29 @@ } }, "@vue/compiler-dom": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.38.tgz", - "integrity": "sha512-zqX4FgUbw56kzHlgYuEEJR8mefFiiyR3u96498+zWPsLeh1WKvgIReoNE+U7gG8bCUdvsrJ0JRmev0Ky6n2O0g==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.41.tgz", + "integrity": "sha512-xe5TbbIsonjENxJsYRbDJvthzqxLNk+tb3d/c47zgREDa/PCp6/Y4gC/skM4H6PIuX5DAxm7fFJdbjjUH2QTMw==", "dev": true, "optional": true, "requires": { - "@vue/compiler-core": "3.2.38", - "@vue/shared": "3.2.38" + "@vue/compiler-core": "3.2.41", + "@vue/shared": "3.2.41" } }, "@vue/compiler-sfc": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.38.tgz", - "integrity": "sha512-KZjrW32KloMYtTcHAFuw3CqsyWc5X6seb8KbkANSWt3Cz9p2qA8c1GJpSkksFP9ABb6an0FLCFl46ZFXx3kKpg==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.41.tgz", + "integrity": "sha512-+1P2m5kxOeaxVmJNXnBskAn3BenbTmbxBxWOtBq3mQTCokIreuMULFantBUclP0+KnzNCMOvcnKinqQZmiOF8w==", "dev": true, "optional": true, "requires": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.38", - "@vue/compiler-dom": "3.2.38", - "@vue/compiler-ssr": "3.2.38", - "@vue/reactivity-transform": "3.2.38", - "@vue/shared": "3.2.38", + "@vue/compiler-core": "3.2.41", + "@vue/compiler-dom": "3.2.41", + "@vue/compiler-ssr": "3.2.41", + "@vue/reactivity-transform": "3.2.41", + "@vue/shared": "3.2.41", "estree-walker": "^2.0.2", "magic-string": "^0.25.7", "postcss": "^8.1.10", @@ -24391,34 +27178,34 @@ } }, "@vue/compiler-ssr": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.38.tgz", - "integrity": "sha512-bm9jOeyv1H3UskNm4S6IfueKjUNFmi2kRweFIGnqaGkkRePjwEcfCVqyS3roe7HvF4ugsEkhf4+kIvDhip6XzQ==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.41.tgz", + "integrity": "sha512-Y5wPiNIiaMz/sps8+DmhaKfDm1xgj6GrH99z4gq2LQenfVQcYXmHIOBcs5qPwl7jaW3SUQWjkAPKMfQemEQZwQ==", "dev": true, "optional": true, "requires": { - "@vue/compiler-dom": "3.2.38", - "@vue/shared": "3.2.38" + "@vue/compiler-dom": "3.2.41", + "@vue/shared": "3.2.41" } }, "@vue/reactivity-transform": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.38.tgz", - "integrity": "sha512-3SD3Jmi1yXrDwiNJqQ6fs1x61WsDLqVk4NyKVz78mkaIRh6d3IqtRnptgRfXn+Fzf+m6B1KxBYWq1APj6h4qeA==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.41.tgz", + "integrity": "sha512-mK5+BNMsL4hHi+IR3Ft/ho6Za+L3FA5j8WvreJ7XzHrqkPq8jtF/SMo7tuc9gHjLDwKZX1nP1JQOKo9IEAn54A==", "dev": true, "optional": true, "requires": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.38", - "@vue/shared": "3.2.38", + "@vue/compiler-core": "3.2.41", + "@vue/shared": "3.2.41", "estree-walker": "^2.0.2", "magic-string": "^0.25.7" } }, "@vue/shared": { - "version": "3.2.38", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.38.tgz", - "integrity": "sha512-dTyhTIRmGXBjxJE+skC8tTWCGLCVc4wQgRRLt8+O9p5ewBAjoBwtCAkLPrtToSr1xltoe3st21Pv953aOZ7alg==", + "version": "3.2.41", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.41.tgz", + "integrity": "sha512-W9mfWLHmJhkfAmV+7gDjcHeAWALQtgGT3JErxULl0oz6R6+3ug91I7IErs93eCFhPCZPHBs4QJS7YWEV7A3sxw==", "dev": true, "optional": true }, @@ -24434,43 +27221,219 @@ "browserstack-local": "^1.4.5", "got": "^11.0.2", "webdriverio": "7.16.16" + }, + "dependencies": { + "@wdio/config": { + "version": "7.16.16", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.16.16.tgz", + "integrity": "sha512-K/ObPuo6Da2liz++OKOIfbdpFwI7UWiFcBylfJkCYbweuXCoW1aUqlKI6rmKPwCH9Uqr/RHWu6p8eo0zWe6xVA==", + "dev": true, + "requires": { + "@wdio/logger": "7.16.0", + "@wdio/types": "7.16.14", + "deepmerge": "^4.0.0", + "glob": "^7.1.2" + } + }, + "@wdio/protocols": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.16.7.tgz", + "integrity": "sha512-Wv40pNQcLiPzQ3o98Mv4A8T1EBQ6k4khglz/e2r16CTm+F3DDYh8eLMAsU5cgnmuwwDKX1EyOiFwieykBn5MCg==", + "dev": true + }, + "@wdio/repl": { + "version": "7.16.14", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.16.14.tgz", + "integrity": "sha512-Ezih0Y+lsGkKv3H3U56hdWgZiQGA3VaAYguSLd9+g1xbQq+zMKqSmfqECD9bAy+OgCCiVTRstES6lHZxJVPhAg==", + "dev": true, + "requires": { + "@wdio/utils": "7.16.14" + } + }, + "@wdio/utils": { + "version": "7.16.14", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.16.14.tgz", + "integrity": "sha512-wwin8nVpIlhmXJkq6GJw9aDDzgLOJKgXTcEua0T2sdXjoW78u5Ly/GZrFXTjMGhacFvoZfitTrjyfyy4CxMVvw==", + "dev": true, + "requires": { + "@wdio/logger": "7.16.0", + "@wdio/types": "7.16.14", + "p-iteration": "^1.1.8" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "devtools": { + "version": "7.16.16", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.16.16.tgz", + "integrity": "sha512-M0kzkuSgfEhpqIis3gdtWsNjn/HQ+vRAmEzDnbYx/7FfjFxhSv1d+rOOT20pvd60soItMYpsOova1igACEGkGQ==", + "dev": true, + "requires": { + "@types/node": "^17.0.4", + "@types/ua-parser-js": "^0.7.33", + "@wdio/config": "7.16.16", + "@wdio/logger": "7.16.0", + "@wdio/protocols": "7.16.7", + "@wdio/types": "7.16.14", + "@wdio/utils": "7.16.14", + "chrome-launcher": "^0.15.0", + "edge-paths": "^2.1.0", + "puppeteer-core": "^13.1.3", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^8.0.0" + } + }, + "devtools-protocol": { + "version": "0.0.973690", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.973690.tgz", + "integrity": "sha512-myh3hSFp0YWa2GED11PmbLhV4dv9RdO7YUz27XJrbQLnP5bMbZL6dfOOILTHO57yH0kX5GfuOZBsg/4NamfPvQ==", + "dev": true + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ua-parser-js": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.32.tgz", + "integrity": "sha512-dXVsz3M4j+5tTiovFVyVqssXBu5HM47//YSOeZ9fQkdDKkfzv2v3PP1jmH6FUyPW+yCSn7aBVK1fGGKNhowdDA==", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "webdriver": { + "version": "7.16.16", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.16.16.tgz", + "integrity": "sha512-x8UoG9k/P8KDrfSh1pOyNevt9tns3zexoMxp9cKnyA/7HYSErhZYTLGlgxscAXLtQG41cMH/Ba/oBmOx7Hgd8w==", + "dev": true, + "requires": { + "@types/node": "^17.0.4", + "@wdio/config": "7.16.16", + "@wdio/logger": "7.16.0", + "@wdio/protocols": "7.16.7", + "@wdio/types": "7.16.14", + "@wdio/utils": "7.16.14", + "got": "^11.0.2", + "ky": "^0.29.0", + "lodash.merge": "^4.6.1" + } + }, + "webdriverio": { + "version": "7.16.16", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.16.16.tgz", + "integrity": "sha512-caPaEWyuD3Qoa7YkW4xCCQA4v9Pa9wmhFGPvNZh3ERtjMCNi8L/XXOdkekWNZmFh3tY0kFguBj7+fAwSY7HAGw==", + "dev": true, + "requires": { + "@types/aria-query": "^5.0.0", + "@types/node": "^17.0.4", + "@wdio/config": "7.16.16", + "@wdio/logger": "7.16.0", + "@wdio/protocols": "7.16.7", + "@wdio/repl": "7.16.14", + "@wdio/types": "7.16.14", + "@wdio/utils": "7.16.14", + "archiver": "^5.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools": "7.16.16", + "devtools-protocol": "^0.0.973690", + "fs-extra": "^10.0.0", + "get-port": "^5.1.1", + "grapheme-splitter": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "lodash.isobject": "^3.0.2", + "lodash.isplainobject": "^4.0.6", + "lodash.zip": "^4.2.0", + "minimatch": "^5.0.0", + "puppeteer-core": "^13.1.3", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^8.0.0", + "webdriver": "7.16.16" + } + } } }, "@wdio/cli": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.16.16.tgz", - "integrity": "sha512-Wz/e5zm1UNHB9RAIsJIM7ioDzVllUwTvhVWOrI7HR/53GmO/cIvAVjpnlglizJNgK8WlbnM/cKNVIXxqxrnFmw==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.5.7.tgz", + "integrity": "sha512-nOQJLskrY+UECDd3NxE7oBzb6cDA7e7x02YWQugOlOgnZ4a+PJmkFoSsO8C2uNCpdFngy5rJKGUo5vbtAHEF9Q==", "dev": true, "requires": { "@types/ejs": "^3.0.5", "@types/fs-extra": "^9.0.4", - "@types/inquirer": "^8.1.2", + "@types/inquirer": "^7.3.1", "@types/lodash.flattendeep": "^4.4.6", "@types/lodash.pickby": "^4.6.6", "@types/lodash.union": "^4.6.6", - "@types/node": "^17.0.4", "@types/recursive-readdir": "^2.2.0", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", "async-exit-hook": "^2.0.1", "chalk": "^4.0.0", "chokidar": "^3.0.0", "cli-spinners": "^2.1.0", "ejs": "^3.0.1", "fs-extra": "^10.0.0", - "inquirer": "8.1.5", + "inquirer": "^8.0.0", "lodash.flattendeep": "^4.4.0", "lodash.pickby": "^4.6.0", "lodash.union": "^4.6.0", "mkdirp": "^1.0.4", "recursive-readdir": "^2.2.2", - "webdriverio": "7.16.16", + "webdriverio": "7.5.7", "yargs": "^17.0.0", "yarn-install": "^1.0.0" }, "dependencies": { + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -24480,6 +27443,16 @@ "color-convert": "^2.0.1" } }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -24490,6 +27463,31 @@ "supports-color": "^7.1.0" } }, + "chrome-launcher": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", + "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "dev": true, + "requires": { + "@types/node": "*", + "escape-string-regexp": "^1.0.5", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^0.5.3", + "rimraf": "^3.0.2" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + } + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -24505,6 +27503,65 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "devtools": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", + "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "dev": true, + "requires": { + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "chrome-launcher": "^0.13.1", + "edge-paths": "^2.1.0", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^0.7.21", + "uuid": "^8.0.0" + } + }, + "devtools-protocol": { + "version": "0.0.878340", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", + "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "puppeteer-core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", + "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.869402", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.869402", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", + "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", + "dev": true + } + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -24514,13 +27571,62 @@ "has-flag": "^4.0.0" } }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "webdriverio": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", + "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", + "dev": true, + "requires": { + "@types/aria-query": "^4.2.1", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/repl": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "archiver": "^5.0.0", + "aria-query": "^4.2.2", + "atob": "^2.1.2", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools": "7.5.7", + "devtools-protocol": "^0.0.878340", + "fs-extra": "^10.0.0", + "get-port": "^5.1.1", + "grapheme-splitter": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "lodash.isobject": "^3.0.2", + "lodash.isplainobject": "^4.0.6", + "lodash.zip": "^4.2.0", + "minimatch": "^3.0.4", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^8.0.0", + "webdriver": "7.5.3" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "requires": {} + }, "yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", + "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", "dev": true, "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", @@ -24532,17 +27638,26 @@ } }, "@wdio/concise-reporter": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.16.14.tgz", - "integrity": "sha512-CR+9+skJ3mXPIdRo0AnIJTJHOArrWdKlXTnyZ/DD6M9VrNk5aiTWQyphT/IeHV5+fxjHlMNIf/KgIhj1ewschQ==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.5.7.tgz", + "integrity": "sha512-964i7eQ4sboSla2bdR8714Er82QBgS6u39GmDFX8Izy9Ge38xaE75HuF5S7mnOWGzSojCWgqtwy5k7Rfg6GE3g==", "dev": true, "requires": { - "@wdio/reporter": "7.16.14", - "@wdio/types": "7.16.14", + "@wdio/reporter": "7.5.7", + "@wdio/types": "7.5.3", "chalk": "^4.0.0", "pretty-ms": "^7.0.0" }, "dependencies": { + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -24577,6 +27692,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -24589,31 +27710,175 @@ } }, "@wdio/config": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.16.16.tgz", - "integrity": "sha512-K/ObPuo6Da2liz++OKOIfbdpFwI7UWiFcBylfJkCYbweuXCoW1aUqlKI6rmKPwCH9Uqr/RHWu6p8eo0zWe6xVA==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.5.3.tgz", + "integrity": "sha512-udvVizYoilOxuWj/BmoN6y7ZCd4wPdYNlSfWznrbCezAdaLZ4/pNDOO0WRWx2C4+q1wdkXZV/VuQPUGfL0lEHQ==", "dev": true, "requires": { - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", "deepmerge": "^4.0.0", "glob": "^7.1.2" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "@wdio/local-runner": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.16.16.tgz", - "integrity": "sha512-AJaOyM842PWgMffrrXyHJjouVseLHoiL5U1sw2VVproi3ORWHbltl1AMnreU/lrGu9L0CVKHYT1pxu5UbSOCxQ==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.5.7.tgz", + "integrity": "sha512-aYc0XUV+/e3cg8Fp+CWlC4FbwSSG3mKAv1iuy/+Hwzg2kJE+aa+Rf2p2BQYc7HPRtKNW0bM8o+aCImZLAiPM+A==", "dev": true, "requires": { "@types/stream-buffers": "^3.0.3", - "@wdio/logger": "7.16.0", - "@wdio/repl": "7.16.14", - "@wdio/runner": "7.16.16", - "@wdio/types": "7.16.14", + "@wdio/logger": "7.5.3", + "@wdio/repl": "7.5.3", + "@wdio/runner": "7.5.7", + "@wdio/types": "7.5.3", "async-exit-hook": "^2.0.1", - "split2": "^4.0.0", + "split2": "^3.2.2", "stream-buffers": "^3.0.2" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "@wdio/logger": { @@ -24662,6 +27927,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -24674,19 +27945,46 @@ } }, "@wdio/mocha-framework": { - "version": "7.16.15", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.16.15.tgz", - "integrity": "sha512-XRya85/RYPZk4MZ7Cvl3oudTdrOo+RyO8b5Ff+dH8hD3GBCACaWgW9AjbsyhvbSTdUlF0gNLPdqOCsxV5XyM3w==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.5.3.tgz", + "integrity": "sha512-96QCVWsiyZxEgOZP3oTq2B2T7zne5dCdehLa2n4q/BLjk96Rj0jifidJZfd/1+vdNPKX0gWWAzpy98Znn8MVMw==", "dev": true, "requires": { - "@types/mocha": "^9.0.0", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", - "expect-webdriverio": "^3.0.0", - "mocha": "^9.0.0" + "@types/mocha": "^8.0.0", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "expect-webdriverio": "^2.0.0", + "mocha": "^8.0.1" }, "dependencies": { + "@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -24710,17 +28008,33 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, "color-convert": { @@ -24738,6 +28052,21 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -24754,10 +28083,30 @@ "path-exists": "^4.0.0" } }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -24773,13 +28122,12 @@ } }, "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "chalk": "^4.0.0" } }, "minimatch": { @@ -24792,47 +28140,59 @@ } }, "mocha": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.1.tgz", - "integrity": "sha512-T7uscqjJVS46Pq1XDXyo9Uvey9gd3huT/DD9cYBb4K2Xc/vbKRPUWK067bxDQRK0yIz6Jxk73IrnimvASzBNAQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", + "chokidar": "3.5.1", + "debug": "4.3.1", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.2.0", + "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.2.0", - "serialize-javascript": "6.0.0", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.2.0", + "wide-align": "1.1.3", + "workerpool": "6.1.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true }, "p-limit": { @@ -24853,11 +28213,23 @@ "p-limit": "^3.0.2" } }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } }, "strip-json-comments": { "version": "3.1.1", @@ -24865,6 +28237,21 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -24889,72 +28276,288 @@ } }, "@wdio/protocols": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.16.7.tgz", - "integrity": "sha512-Wv40pNQcLiPzQ3o98Mv4A8T1EBQ6k4khglz/e2r16CTm+F3DDYh8eLMAsU5cgnmuwwDKX1EyOiFwieykBn5MCg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.5.3.tgz", + "integrity": "sha512-lpNaKwxYhDSL6neDtQQYXvzMAw+u4PXx65ryeMEX82mkARgzSZps5Kyrg9ub7X4T17K1NPfnY6UhZEWg6cKJCg==", "dev": true }, "@wdio/repl": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.16.14.tgz", - "integrity": "sha512-Ezih0Y+lsGkKv3H3U56hdWgZiQGA3VaAYguSLd9+g1xbQq+zMKqSmfqECD9bAy+OgCCiVTRstES6lHZxJVPhAg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.5.3.tgz", + "integrity": "sha512-jfNJwNoc2nWdnLsFoGHmOJR9zaWfDTBMWM3W1eR5kXIjevD6gAfWsB5ZoA4IdybujCXxdnhlsm4o2jIzp/6f7A==", "dev": true, "requires": { - "@wdio/utils": "7.16.14" + "@wdio/utils": "7.5.3" } }, "@wdio/reporter": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.16.14.tgz", - "integrity": "sha512-e/I2oGfqjx9+zI4NT/garqxm7Afnos1EcrGSNu75WmP3PNJt4i+9DKkROu4PM6XWcpUB4v2UF7Mv/NrL3TU9aA==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.5.7.tgz", + "integrity": "sha512-9PXqZtCXDtU6UYLNDPu9MZQ8BiABGnRlJTrlbYB3gBfZDibMkJMvwXzPderipBv2+ifDZXmGe3Njf1ao2TkbFA==", "dev": true, "requires": { - "@types/diff": "^5.0.0", - "@types/node": "^17.0.4", - "@types/object-inspect": "^1.8.0", - "@types/supports-color": "^8.1.0", - "@types/tmp": "^0.2.0", - "@wdio/types": "7.16.14", - "diff": "^5.0.0", - "fs-extra": "^10.0.0", - "object-inspect": "^1.10.3", - "supports-color": "8.1.1" + "@wdio/types": "7.5.3", + "fs-extra": "^10.0.0" + }, + "dependencies": { + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + } } }, "@wdio/runner": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.16.16.tgz", - "integrity": "sha512-Tt2ja6GukGPq1m98WP26yOWUGwzK1y7gPTLy6rKlamz3mOBC7koL0T9+iqcFREquUe4CMy2jWp1lqvPlwMbu7g==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.5.7.tgz", + "integrity": "sha512-RzVXd+xnwK/thkx1/xo9K5iscQ0Ofobgsx5dNVtwLDVMn9V7jCW/WX4dSCPAPaVSqnUCmkcQp3P5AoSBPpCZnQ==", "dev": true, "requires": { - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", "deepmerge": "^4.0.0", "gaze": "^1.1.2", - "webdriver": "7.16.16", - "webdriverio": "7.16.16" + "webdriver": "7.5.3", + "webdriverio": "7.5.7" + }, + "dependencies": { + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chrome-launcher": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", + "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "dev": true, + "requires": { + "@types/node": "*", + "escape-string-regexp": "^1.0.5", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^0.5.3", + "rimraf": "^3.0.2" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "devtools": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", + "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "dev": true, + "requires": { + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "chrome-launcher": "^0.13.1", + "edge-paths": "^2.1.0", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^0.7.21", + "uuid": "^8.0.0" + } + }, + "devtools-protocol": { + "version": "0.0.878340", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", + "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "puppeteer-core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", + "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.869402", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.869402", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", + "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", + "dev": true + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "webdriverio": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", + "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", + "dev": true, + "requires": { + "@types/aria-query": "^4.2.1", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/repl": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "archiver": "^5.0.0", + "aria-query": "^4.2.2", + "atob": "^2.1.2", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools": "7.5.7", + "devtools-protocol": "^0.0.878340", + "fs-extra": "^10.0.0", + "get-port": "^5.1.1", + "grapheme-splitter": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "lodash.isobject": "^3.0.2", + "lodash.isplainobject": "^4.0.6", + "lodash.zip": "^4.2.0", + "minimatch": "^3.0.4", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^8.0.0", + "webdriver": "7.5.3" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "requires": {} + } } }, "@wdio/spec-reporter": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.19.1.tgz", - "integrity": "sha512-qnZkn3VcyBPtcorUtpyCFE8v5ubyWmR7mFETXNzyriHyvjvk+NeFCWaFcIehpXYXiAmNpAwyfnZoIY6tkKQixQ==", + "version": "7.19.7", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.19.7.tgz", + "integrity": "sha512-BDBZU2EK/GuC9VxtfqPtoW43FmvKxYDsvcDVDi3F7o+9fkcuGSJiWbw1AX251ZzzVQ7YP9ImTitSpdpUKXkilQ==", "dev": true, "requires": { "@types/easy-table": "^0.0.33", - "@wdio/reporter": "7.19.1", - "@wdio/types": "7.19.1", + "@wdio/reporter": "7.19.7", + "@wdio/types": "7.19.5", "chalk": "^4.0.0", "easy-table": "^1.1.1", "pretty-ms": "^7.0.0" }, "dependencies": { "@wdio/reporter": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.19.1.tgz", - "integrity": "sha512-sWmBBV4dPCZkGk9Qq0m35T/vHGen0N10nH4osQcVP3IZJqpo2eLIH4w+X6EUbjZ2GdgOA2bLMMzb1bl9JqnGPg==", + "version": "7.19.7", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.19.7.tgz", + "integrity": "sha512-Dum19gpfru66FnIq78/4HTuW87B7ceLDp6PJXwQM5kXyN7Gb7zhMgp6FZTM0FCYLyi6U/zXZSvpNUYl77caS6g==", "dev": true, "requires": { "@types/diff": "^5.0.0", @@ -24962,17 +28565,46 @@ "@types/object-inspect": "^1.8.0", "@types/supports-color": "^8.1.0", "@types/tmp": "^0.2.0", - "@wdio/types": "7.19.1", + "@wdio/types": "7.19.5", "diff": "^5.0.0", "fs-extra": "^10.0.0", "object-inspect": "^1.10.3", "supports-color": "8.1.1" + } + }, + "@wdio/types": { + "version": "7.19.5", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.19.5.tgz", + "integrity": "sha512-S1lC0pmtEO7NVH/2nM1c7NHbkgxLZH3VVG/z6ym3Bbxdtcqi2LMsEvvawMAU/fmhyiIkMsGZCO8vxG9cRw4z4A==", + "dev": true, + "requires": { + "@types/node": "^17.0.4", + "got": "^11.8.1" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "dependencies": { "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -24980,13 +28612,303 @@ } } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@wdio/sync": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.5.7.tgz", + "integrity": "sha512-Zu/AYLjwqbFSbaOU1US7ownv3ov8JrtoGHq51JfJ4masefJDXNkHix2cZ0qEgl3IvkkWQ0ewL0G8GTXb3KOemA==", + "dev": true, + "requires": { + "@types/fibers": "^3.1.0", + "@types/puppeteer": "^5.4.0", + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3", + "fibers": "^5.0.0", + "webdriverio": "7.5.7" + }, + "dependencies": { + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chrome-launcher": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", + "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "dev": true, + "requires": { + "@types/node": "*", + "escape-string-regexp": "^1.0.5", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^0.5.3", + "rimraf": "^3.0.2" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "devtools": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", + "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "dev": true, + "requires": { + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "chrome-launcher": "^0.13.1", + "edge-paths": "^2.1.0", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^0.7.21", + "uuid": "^8.0.0" + } + }, + "devtools-protocol": { + "version": "0.0.878340", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", + "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "puppeteer-core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", + "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.869402", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.869402", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", + "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", + "dev": true + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "webdriverio": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", + "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", + "dev": true, + "requires": { + "@types/aria-query": "^4.2.1", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/repl": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", + "archiver": "^5.0.0", + "aria-query": "^4.2.2", + "atob": "^2.1.2", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools": "7.5.7", + "devtools-protocol": "^0.0.878340", + "fs-extra": "^10.0.0", + "get-port": "^5.1.1", + "grapheme-splitter": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "lodash.isobject": "^3.0.2", + "lodash.isplainobject": "^4.0.6", + "lodash.zip": "^4.2.0", + "minimatch": "^3.0.4", + "puppeteer-core": "^9.1.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^8.0.0", + "webdriver": "7.5.3" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "requires": {} + } + } + }, + "@wdio/types": { + "version": "7.16.14", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.16.14.tgz", + "integrity": "sha512-AyNI9iBSos9xWBmiFAF3sBs6AJXO/55VppU/eeF4HRdbZMtMarnvMuahM+jlUrA3vJSmDW+ufelG0MT//6vrnw==", + "dev": true, + "requires": { + "@types/node": "^17.0.4", + "got": "^11.8.1" + } + }, + "@wdio/utils": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.5.3.tgz", + "integrity": "sha512-nlLDKr8v8abLOHCKroBwQkGPdCIxjID2MllgWX23xqkYZylM9RdwPBdL8osQt9m3rq2TxiPAT4OlbzNt2WtN6Q==", + "dev": true, + "requires": { + "@wdio/logger": "7.5.3", + "@wdio/types": "7.5.3" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, "@wdio/types": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.19.1.tgz", - "integrity": "sha512-mOodKlmvYxpj8P5BhjggEGpXuiRSlsyn2ClG8QqJ3lfXgOtOVEzFNfv/Ai7TkHr+lHDQNXLjllCjSqoCHhwlqg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", "dev": true, "requires": { - "@types/node": "^17.0.4", "got": "^11.8.1" } }, @@ -25024,6 +28946,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -25035,41 +28963,6 @@ } } }, - "@wdio/sync": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.16.16.tgz", - "integrity": "sha512-MbVFAteaAOxHLKkMiMzOnh1hzINAK2U41GDIfy1yaPumcw1pNuJIhWrBYxprNMlqt8srk++wqQWgj5XpFjCL6g==", - "dev": true, - "requires": { - "@types/fibers": "^3.1.0", - "@types/puppeteer": "^5.4.0", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "fibers": "^5.0.0", - "webdriverio": "7.16.16" - } - }, - "@wdio/types": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.16.14.tgz", - "integrity": "sha512-AyNI9iBSos9xWBmiFAF3sBs6AJXO/55VppU/eeF4HRdbZMtMarnvMuahM+jlUrA3vJSmDW+ufelG0MT//6vrnw==", - "dev": true, - "requires": { - "@types/node": "^17.0.4", - "got": "^11.8.1" - } - }, - "@wdio/utils": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.16.14.tgz", - "integrity": "sha512-wwin8nVpIlhmXJkq6GJw9aDDzgLOJKgXTcEua0T2sdXjoW78u5Ly/GZrFXTjMGhacFvoZfitTrjyfyy4CxMVvw==", - "dev": true, - "requires": { - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "p-iteration": "^1.1.8" - } - }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -25217,9 +29110,9 @@ } }, "@xmldom/xmldom": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.6.tgz", - "integrity": "sha512-HHXP9hskkFQHy8QxxUXkS7946FFIhYVfGqsk0WLwllmexN9x/+R4UBLvurHEuyXRfVEObVR8APuQehykLviwSQ==", + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.8.tgz", + "integrity": "sha512-PrJx38EfpitFhwmILRl37jAdBlsww6AZ6rRVK4QS7T7RHLhX7mSs647sTmgr9GIxe3qjXdesmomEgbgaokrVFg==", "dev": true }, "@xtuc/ieee754": { @@ -25237,7 +29130,7 @@ "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", "dev": true }, "accepts": { @@ -25262,6 +29155,12 @@ "dev": true, "requires": {} }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, "aes-decrypter": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.1.3.tgz", @@ -25275,10 +29174,13 @@ } }, "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } }, "ajv": { "version": "6.12.3", @@ -25292,17 +29194,24 @@ "uri-js": "^4.2.2" } }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", "dev": true, "optional": true }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-cyan": { @@ -25326,7 +29235,7 @@ "ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", "dev": true, "requires": { "ansi-wrap": "0.1.0" @@ -25358,7 +29267,7 @@ "ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", "dev": true }, "anymatch": { @@ -25374,20 +29283,20 @@ "append-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", "dev": true, "requires": { "buffer-equal": "^1.0.0" } }, "archiver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz", - "integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", + "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", "dev": true, "requires": { "archiver-utils": "^2.1.0", - "async": "^3.2.0", + "async": "^3.2.3", "buffer-crc32": "^0.2.1", "readable-stream": "^3.6.0", "readdir-glob": "^1.0.0", @@ -25396,9 +29305,9 @@ }, "dependencies": { "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "readable-stream": { @@ -25435,7 +29344,7 @@ "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, "argparse": { @@ -25448,21 +29357,36 @@ } }, "aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", - "dev": true + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "requires": { + "deep-equal": "^2.0.5" + } }, "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "dependencies": { + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", + "dev": true + } + } }, "arr-filter": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", "dev": true, "requires": { "make-iterator": "^1.0.0" @@ -25477,50 +29401,50 @@ "arr-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", "dev": true, "requires": { "make-iterator": "^1.0.0" } }, "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", "dev": true }, "array-differ": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", "dev": true }, "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", "dev": true }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-from": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==", "dev": true }, "array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", "get-intrinsic": "^1.1.1", "is-string": "^1.0.7" } @@ -25528,7 +29452,7 @@ "array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", - "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "integrity": "sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==", "dev": true, "requires": { "array-slice": "^1.0.0", @@ -25580,24 +29504,25 @@ "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", "dev": true }, "array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, "asn1": { @@ -25624,7 +29549,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "assertion-error": { @@ -25636,7 +29561,7 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", "dev": true }, "astral-regex": { @@ -25648,7 +29573,7 @@ "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", "dev": true }, "async-done": { @@ -25678,7 +29603,7 @@ "async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", - "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "integrity": "sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==", "dev": true, "requires": { "async-done": "^1.2.2" @@ -25687,7 +29612,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "atob": { @@ -25705,7 +29630,7 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true }, "aws4": { @@ -25717,7 +29642,7 @@ "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", "dev": true, "requires": { "chalk": "^1.1.3", @@ -25728,19 +29653,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -25753,13 +29678,13 @@ "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", "dev": true }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -25768,7 +29693,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true } } @@ -25812,19 +29737,13 @@ "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", "dev": true }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -25845,19 +29764,10 @@ "trim-right": "^1.0.1" }, "dependencies": { - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==", "dev": true } } @@ -25865,7 +29775,7 @@ "babel-helpers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==", "dev": true, "requires": { "babel-runtime": "^6.22.0", @@ -25873,56 +29783,26 @@ } }, "babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dev": true, "requires": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } } }, "babel-messages": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - }, "babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -25934,54 +29814,39 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", - "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - } } }, "babel-plugin-polyfill-corejs2": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", - "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "requires": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.3.1", + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" } }, "babel-plugin-polyfill-corejs3": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", - "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", - "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.1" + "@babel/helper-define-polyfill-provider": "^0.3.3" } }, "babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==", "dev": true, "requires": { "babel-core": "^6.26.0", @@ -26007,22 +29872,13 @@ "requires": { "minimist": "^1.2.6" } - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } } } }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", "dev": true, "requires": { "core-js": "^2.4.0", @@ -26034,13 +29890,19 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true } } }, "babel-template": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", "dev": true, "requires": { "babel-runtime": "^6.26.0", @@ -26053,7 +29915,7 @@ "babel-traverse": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", "dev": true, "requires": { "babel-code-frame": "^6.26.0", @@ -26085,7 +29947,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -26093,7 +29955,7 @@ "babel-types": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", "dev": true, "requires": { "babel-runtime": "^6.26.0", @@ -26105,7 +29967,7 @@ "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", "dev": true } } @@ -26119,7 +29981,7 @@ "bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", - "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "integrity": "sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==", "dev": true, "requires": { "arr-filter": "^1.1.1", @@ -26163,7 +30025,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, "requires": { "is-descriptor": "^1.0.0" @@ -26190,18 +30052,26 @@ "dev": true, "requires": { "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "requires": { "tweetnacl": "^0.14.3" @@ -26210,7 +30080,7 @@ "beeper": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "integrity": "sha512-3vqtKL1N45I5dV0RdssXZG7X6pCqQrWPNOlBPZPrd+QkE2HEhR57Z04m0KtpbsZH73j+a3F8UD1TQnn+ExTvIA==", "dev": true }, "big-integer": { @@ -26228,7 +30098,7 @@ "binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", "dev": true, "requires": { "buffers": "~0.1.1", @@ -26284,13 +30154,13 @@ "bluebird": { "version": "3.4.7", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", "dev": true }, "body": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", + "integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==", "dev": true, "requires": { "continuable-cache": "^0.3.1", @@ -26302,13 +30172,13 @@ "bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", + "integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==", "dev": true }, "raw-body": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", + "integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==", "dev": true, "requires": { "bytes": "1", @@ -26318,26 +30188,28 @@ "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true } } }, "body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { "debug": { @@ -26351,7 +30223,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -26381,14 +30253,14 @@ "dev": true }, "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" } }, "browserstack": { @@ -26431,21 +30303,22 @@ } }, "browserstack-local": { - "version": "1.4.9", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.9.tgz", - "integrity": "sha512-V+q8HQwRQFr9nd32xR66ZZ3VDWa3Kct4IMMudhKgcuD7cWrvvFARZOibx71II+Rf7P5nMQpWWxl9z/3p927nbg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.5.1.tgz", + "integrity": "sha512-T/wxyWDzvBHbDvl7fZKpFU7mYze6nrUkBhNy+d+8bXBqgQX10HTYvajIGO0wb49oGSLCPM0CMZTV/s7e6LF0sA==", "dev": true, "requires": { - "https-proxy-agent": "^4.0.0", + "agent-base": "^6.0.2", + "https-proxy-agent": "^5.0.1", "is-running": "^2.1.0", "ps-tree": "=1.2.0", "temp-fs": "^0.9.9" } }, "browserstacktunnel-wrapper": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.4.tgz", - "integrity": "sha512-GCV599FUUxNOCFl3WgPnfc5dcqq9XTmMXoxWpqkvmk0R9TOIoqmjENNU6LY6DtgIL6WfBVbg/jmWtnM5K6UYSg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.5.tgz", + "integrity": "sha512-oociT3nl+FhQnyJbAb1RM4oQ5pN7aKeXEURkTkiEVm/Rji2r0agl3Wbw5V23VFn9lCU5/fGyDejRZPtGYsEcFw==", "dev": true, "requires": { "https-proxy-agent": "^2.2.1", @@ -26495,13 +30368,13 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", "dev": true }, "buffer-from": { @@ -26519,7 +30392,7 @@ "buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", "dev": true }, "bytes": { @@ -26530,7 +30403,7 @@ "cac": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz", - "integrity": "sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8=", + "integrity": "sha512-hq4rxE3NT5PlaEiVV39Z45d6MoFcQZG5dsgJqtAUeOz3408LEQAElToDkf9i5IYSCOmK0If/81dLg7nKxqPR0w==", "dev": true, "requires": { "camelcase-keys": "^3.0.0", @@ -26545,35 +30418,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, - "camelcase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-3.0.0.tgz", - "integrity": "sha1-/AxsNgNj9zd+N5O5oWvM8QcMHKQ=", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "map-obj": "^1.0.0" - } - }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -26586,23 +30443,35 @@ "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", "dev": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" } }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", "dev": true, "requires": { "pinkie-promise": "^2.0.0" @@ -26611,7 +30480,7 @@ "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", "dev": true, "requires": { "load-json-file": "^1.0.0", @@ -26622,17 +30491,23 @@ "read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", "dev": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" } }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -26641,7 +30516,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true } } @@ -26716,6 +30591,24 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "camelcase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-3.0.0.tgz", + "integrity": "sha512-U4E6A6aFyYnNW+tDt5/yIUKQURKXe3WMFPfX4FxrQFcwZ/R08AUk1xWcUtlr7oq6CV07Ji+aa69V2g7BSpblnQ==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true + } + } + }, "can-autoplay": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/can-autoplay/-/can-autoplay-3.0.2.tgz", @@ -26723,14 +30616,14 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001390", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001390.tgz", - "integrity": "sha512-sS4CaUM+/+vqQUlCvCJ2WtDlV81aWtHhqeEVkLokVJJa3ViN4zDxAGfq9R8i1m90uGHxo99cy10Od+lvn3hf0g==" + "version": "1.0.30001429", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001429.tgz", + "integrity": "sha512-511ThLu1hF+5RRRt0zYCf2U2yRr9GPF6m5y90SBCWsvSoYoW7yAGlv/elyPaNfvGCkp6kj/KFZWU0BMA69Prsg==" }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "ccount": { @@ -26757,7 +30650,7 @@ "chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", "dev": true, "requires": { "traverse": ">=0.3.0 <0.4" @@ -26771,21 +30664,6 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } } }, "character-entities": { @@ -26815,7 +30693,7 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true }, "chokidar": { @@ -26841,9 +30719,9 @@ "dev": true }, "chrome-launcher": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.0.tgz", - "integrity": "sha512-ZQqX5kb9H0+jy1OqLnWampfocrtSZaGl7Ny3F9GRha85o4odbL8x55paUzh51UC7cEmZ5obp3H2Mm70uC2PpRA==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.1.tgz", + "integrity": "sha512-UugC8u59/w2AyX5sHLZUHoxBAiSiunUhZa3zZwMH6zPVis0C3dDKiRWyUGIo14tTbZHGVviWxv3PQWZ7taZ4fg==", "dev": true, "requires": { "@types/node": "*", @@ -26878,10 +30756,16 @@ "static-extend": "^0.1.1" }, "dependencies": { + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -26890,7 +30774,7 @@ "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -26899,7 +30783,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -26916,7 +30800,7 @@ "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -26925,7 +30809,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -26956,9 +30840,9 @@ } }, "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", "dev": true }, "cli-width": { @@ -26968,32 +30852,32 @@ "dev": true }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", "dev": true }, "clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", "dev": true }, "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -27002,7 +30886,7 @@ "clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", "dev": true }, "cloneable-readable": { @@ -27019,13 +30903,13 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", "dev": true }, "collection-map": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", - "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "integrity": "sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==", "dev": true, "requires": { "arr-map": "^2.0.2", @@ -27036,7 +30920,7 @@ "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", "dev": true, "requires": { "map-visit": "^1.0.0", @@ -27054,7 +30938,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "color-support": { "version": "1.1.3", @@ -27083,10 +30967,16 @@ "integrity": "sha512-G5yTt3KQN4Yn7Yk4ed73hlZ1evrFKXeUW3086p3PRFNp7m2vIjI6Pg+Kgb+oyzhd9F2qdcoj67+y3SdxL5XWsg==", "dev": true }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "component-emitter": { @@ -27123,7 +31013,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "concat-stream": { @@ -27176,10 +31066,40 @@ "ms": "2.0.0" } }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true } } @@ -27196,13 +31116,6 @@ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } } }, "content-type": { @@ -27213,31 +31126,28 @@ "continuable-cache": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", + "integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA==", "dev": true }, "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "requires": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", "dev": true }, "copy-props": { @@ -27251,30 +31161,22 @@ } }, "core-js": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", - "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==" + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz", + "integrity": "sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==" }, "core-js-compat": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", - "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.0.tgz", + "integrity": "sha512-piOX9Go+Z4f9ZiBFLnZ5VrOpBl0h7IGCkiFUN11QTe6LjAvOT3ifL/5TdoizMh99hcGy5SoLyWbapIY/PIb/3A==", "requires": { - "browserslist": "^4.19.1", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" - } + "browserslist": "^4.21.4" } }, "core-js-pure": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", - "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==" + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.0.tgz", + "integrity": "sha512-LiN6fylpVBVwT8twhhluD9TzXmZQQsr2I2eIKtWNbZI1XMfBT7CV18itaN6RA7EtQd/SDdRx/wzvAShX2HvhQA==" }, "core-util-is": { "version": "1.0.3", @@ -27306,14 +31208,10 @@ } }, "crc-32": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.1.tgz", - "integrity": "sha512-Dn/xm/1vFFgs3nfrpEVScHoIslO9NZRITWGz/1E/St6u4xw99vfZzVkW0OSnzx2h9egej9xwMCEut6sqwokM/w==", - "dev": true, - "requires": { - "exit-on-epipe": "~1.0.1", - "printj": "~1.3.1" - } + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true }, "crc32-stream": { "version": "4.0.2", @@ -27396,13 +31294,13 @@ "css-value": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", - "integrity": "sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo=", + "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", "dev": true }, "custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "dev": true }, "d": { @@ -27418,22 +31316,22 @@ "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "requires": { "assert-plus": "^1.0.0" } }, "date-format": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.4.tgz", - "integrity": "sha512-/jyf4rhB17ge328HJuJjAcmRtCsGd+NDeAtahRBTaK6vSPR6MO5HlrAit3Nn7dVjaa6sowW0WXt8yQtLyZQFRg==", + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", "dev": true }, "dateformat": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "integrity": "sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==", "dev": true }, "de-indent": { @@ -27444,9 +31342,9 @@ "optional": true }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -27474,9 +31372,9 @@ } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "decode-named-character-reference": { @@ -27491,7 +31389,7 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", "dev": true }, "decompress-response": { @@ -27567,13 +31465,13 @@ "default-resolution": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", - "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", "dev": true }, "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, "requires": { "clone": "^1.0.2" @@ -27582,7 +31480,7 @@ "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true } } @@ -27594,11 +31492,13 @@ "dev": true }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "define-property": { @@ -27614,13 +31514,13 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "dequal": { "version": "2.0.3", @@ -27629,85 +31529,232 @@ "dev": true }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", "dev": true }, "devtools": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.16.16.tgz", - "integrity": "sha512-M0kzkuSgfEhpqIis3gdtWsNjn/HQ+vRAmEzDnbYx/7FfjFxhSv1d+rOOT20pvd60soItMYpsOova1igACEGkGQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.25.4.tgz", + "integrity": "sha512-R6/S/dCqxoX4Y6PxIGM9JFAuSRZzUeV5r+CoE/frhmno6mTe7dEEgwkJlfit3LkKRoul8n4DsL2A3QtWOvq5IA==", "dev": true, "requires": { - "@types/node": "^17.0.4", + "@types/node": "^18.0.0", "@types/ua-parser-js": "^0.7.33", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@wdio/config": "7.25.4", + "@wdio/logger": "7.19.0", + "@wdio/protocols": "7.22.0", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", "chrome-launcher": "^0.15.0", "edge-paths": "^2.1.0", "puppeteer-core": "^13.1.3", "query-selector-shadow-dom": "^1.0.0", "ua-parser-js": "^1.0.1", - "uuid": "^8.0.0" + "uuid": "^9.0.0" }, "dependencies": { + "@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "dev": true + }, + "@wdio/config": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.25.4.tgz", + "integrity": "sha512-vb0emDtD9FbFh/yqW6oNdo2iuhQp8XKj6GX9fyy9v4wZgg3B0HPMVJxhIfcoHz7LMBWlHSo9YdvhFI5EQHRLBA==", + "dev": true, + "requires": { + "@wdio/logger": "7.19.0", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", + "deepmerge": "^4.0.0", + "glob": "^8.0.3" + } + }, + "@wdio/logger": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.19.0.tgz", + "integrity": "sha512-xR7SN/kGei1QJD1aagzxs3KMuzNxdT/7LYYx+lt6BII49+fqL/SO+5X0FDCZD0Ds93AuQvvz9eGyzrBI2FFXmQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/protocols": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.22.0.tgz", + "integrity": "sha512-8EXRR+Ymdwousm/VGtW3H1hwxZ/1g1H99A1lF0U4GuJ5cFWHCd0IVE5H31Z52i8ZruouW8jueMkGZPSo2IIUSQ==", + "dev": true + }, + "@wdio/types": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.25.4.tgz", + "integrity": "sha512-muvNmq48QZCvocctnbe0URq2FjJjUPIG4iLoeMmyF0AQgdbjaUkMkw3BHYNHVTbSOU9WMsr2z8alhj/I2H6NRQ==", + "dev": true, + "requires": { + "@types/node": "^18.0.0", + "got": "^11.8.1" + } + }, + "@wdio/utils": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.25.4.tgz", + "integrity": "sha512-8iwQDk+foUqSzKZKfhLxjlCKOkfRJPNHaezQoevNgnrTq/t0ek+ldZCATezb9B8jprAuP4mgS9xi22akc6RkzA==", + "dev": true, + "requires": { + "@wdio/logger": "7.19.0", + "@wdio/types": "7.25.4", + "p-iteration": "^1.1.8" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "ua-parser-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.2.tgz", - "integrity": "sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg==", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.32.tgz", + "integrity": "sha512-dXVsz3M4j+5tTiovFVyVqssXBu5HM47//YSOeZ9fQkdDKkfzv2v3PP1jmH6FUyPW+yCSn7aBVK1fGGKNhowdDA==", "dev": true }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "dev": true } } }, "devtools-protocol": { - "version": "0.0.973690", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.973690.tgz", - "integrity": "sha512-myh3hSFp0YWa2GED11PmbLhV4dv9RdO7YUz27XJrbQLnP5bMbZL6dfOOILTHO57yH0kX5GfuOZBsg/4NamfPvQ==", + "version": "0.0.1061995", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1061995.tgz", + "integrity": "sha512-pKZZWTjWa/IF4ENCg6GN8bu/AxSZgdhjSa26uc23wz38Blt2Tnm9icOPcSG3Cht55rMq35in1w3rWVPcZ60ArA==", "dev": true }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", "dev": true }, "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", "dev": true }, "dlv": { @@ -27796,15 +31843,9 @@ } }, "chalk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", - "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", - "dev": true - }, - "diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", "dev": true }, "glob": { @@ -27820,12 +31861,6 @@ "once": "^1.3.0" } }, - "ini": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", - "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -27844,19 +31879,13 @@ "brace-expansion": "^2.0.1" } }, - "strip-json-comments": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.0.tgz", - "integrity": "sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==", - "dev": true - }, "yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", + "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", "dev": true, "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", @@ -27870,7 +31899,7 @@ "dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", "dev": true, "requires": { "custom-event": "~1.0.0", @@ -27897,12 +31926,38 @@ "dev": true }, "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", "dev": true, "requires": { - "readable-stream": "^2.0.2" + "readable-stream": "~1.1.9" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + } } }, "duplexify": { @@ -27970,7 +32025,7 @@ "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "requires": { "jsbn": "~0.1.0", @@ -27990,7 +32045,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "ejs": { "version": "3.1.8", @@ -28002,14 +32057,14 @@ } }, "electron-to-chromium": { - "version": "1.4.243", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.243.tgz", - "integrity": "sha512-BgLD2gBX43OSXwlT01oYRRD5NIB4n3okTRxkzEAC6G0SZG4TTlyrWMjbOo0fajCwqwpRtMHXQNMjtRN6qpNtfw==" + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "emojis-list": { @@ -28021,7 +32076,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "end-of-stream": { "version": "1.4.4", @@ -28033,9 +32088,9 @@ } }, "engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", "dev": true, "requires": { "@types/cookie": "^0.4.1", @@ -28048,21 +32103,26 @@ "debug": "~4.3.1", "engine.io-parser": "~5.0.3", "ws": "~8.2.3" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + } } }, "engine.io-parser": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", - "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", - "dev": true, - "requires": { - "@socket.io/base64-arraybuffer": "~1.0.2" - } + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "dev": true }, "enhanced-resolve": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", - "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -28081,7 +32141,7 @@ "ent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", "dev": true }, "errno": { @@ -28112,31 +32172,35 @@ } }, "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.2", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" } }, "es-get-iterator": { @@ -28161,6 +32225,15 @@ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -28184,15 +32257,15 @@ } }, "es5-shim": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.5.tgz", - "integrity": "sha512-vfQ4UAai8szn0sAubCy97xnZ4sJVDD1gt/Grn736hg8D7540wemIb1YPrYZSTqlM2H69EQX1or4HU/tSwRTI3w==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.7.tgz", + "integrity": "sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==", "dev": true }, "es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", "dev": true, "requires": { "d": "1", @@ -28203,7 +32276,7 @@ "es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", "dev": true }, "es6-promise": { @@ -28215,7 +32288,7 @@ "es6-promisify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", "dev": true, "requires": { "es6-promise": "^4.0.3" @@ -28251,17 +32324,17 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "escodegen": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", "dev": true, "requires": { "esprima": "^2.7.1", @@ -28274,13 +32347,13 @@ "estraverse": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", "dev": true }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, "requires": { "prelude-ls": "~1.1.2", @@ -28304,13 +32377,13 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true }, "source-map": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", "dev": true, "optional": true, "requires": { @@ -28320,7 +32393,7 @@ "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "requires": { "prelude-ls": "~1.1.2" @@ -28385,18 +32458,6 @@ "@babel/highlight": "^7.10.4" } }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -28438,18 +32499,24 @@ "dev": true }, "globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -28481,7 +32548,7 @@ "eslint-config-standard": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", - "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=", + "integrity": "sha512-UkFojTV1o0GOe1edOEiuI5ccYLJSuNngtqSeClNzhsmG8KPJ+7mRxgtp2oYhqZAK/brlXMoCd+VgXViE0AfyKw==", "dev": true, "requires": {} }, @@ -28507,13 +32574,12 @@ } }, "eslint-module-utils": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", - "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", "dev": true, "requires": { - "debug": "^3.2.7", - "find-up": "^2.1.0" + "debug": "^3.2.7" }, "dependencies": { "debug": { @@ -28538,9 +32604,9 @@ } }, "eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "requires": { "array-includes": "^3.1.4", @@ -28548,14 +32614,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "is-glob": "^4.0.3", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "dependencies": { "debug": { @@ -28579,7 +32645,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -28678,7 +32744,7 @@ "esprima": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", "dev": true }, "esquery": { @@ -28736,12 +32802,12 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", "dev": true, "requires": { "d": "1", @@ -28751,7 +32817,7 @@ "event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", "dev": true, "requires": { "duplexer": "~0.1.1", @@ -28766,7 +32832,7 @@ "map-stream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", "dev": true } } @@ -28814,7 +32880,7 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true }, "semver": { @@ -28826,7 +32892,7 @@ "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -28835,7 +32901,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true }, "which": { @@ -28849,16 +32915,10 @@ } } }, - "exit-on-epipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", - "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==", - "dev": true - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", "dev": true, "requires": { "debug": "^2.3.3", @@ -28882,7 +32942,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -28891,7 +32951,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -28900,7 +32960,7 @@ "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -28909,7 +32969,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -28926,7 +32986,7 @@ "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -28935,7 +32995,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -28957,13 +33017,13 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -28971,66 +33031,95 @@ "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, "requires": { "homedir-polyfill": "^1.0.1" } }, "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", "dev": true, "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } } }, "expect-webdriverio": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-3.1.4.tgz", - "integrity": "sha512-65FTS3bmxcIp0cq6fLb/72TrCQXBCpwPLC7SwMWdpPlLq461mXcK1BPKJJjnIC587aXSKD+3E4hvnlCtwDmXfg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-2.0.2.tgz", + "integrity": "sha512-dst0tqP1aZ2p7TPmbatqoIQ+7hRTw+IeKNi830XxKhu2DNNe5vQ85i9ttf9rpXgbnUf91HxKcocn4G7A5bQxDA==", "dev": true, "requires": { - "expect": "^27.0.2", - "jest-matcher-utils": "^27.0.2" + "expect": "^26.6.2", + "jest-matcher-utils": "^26.6.2" } }, "express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.2", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.2", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.7", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", + "send": "0.18.0", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -29047,28 +33136,23 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, "ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", "dev": true, "requires": { - "type": "^2.5.0" + "type": "^2.7.2" }, "dependencies": { "type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", "dev": true } } @@ -29080,13 +33164,20 @@ "dev": true }, "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "kind-of": "^1.1.0" + }, + "dependencies": { + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true + } } }, "external-editor": { @@ -29098,17 +33189,6 @@ "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" - }, - "dependencies": { - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - } } }, "extglob": { @@ -29130,7 +33210,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, "requires": { "is-descriptor": "^1.0.0" @@ -29139,7 +33219,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -29148,7 +33228,7 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true } } @@ -29179,7 +33259,7 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, "faker": { @@ -29215,13 +33295,13 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", "dev": true, "requires": { "websocket-driver": ">=0.5.1" @@ -29230,16 +33310,16 @@ "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "requires": { "pend": "~1.2.0" } }, "fibers": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.1.tgz", - "integrity": "sha512-VMC7Frt87Oo0AOJ6EcPFbi+tZmkQ4tD85aatwyWL6I9cYMJmm2e+pXUJsfGZ36U7MffXtjou2XIiWJMtHriErw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.3.tgz", + "integrity": "sha512-/qYTSoZydQkM21qZpGLDLuCq8c+B8KhuCQ1kLPvnRNhxhVbvrpmH9l2+Lblf5neDuEsY4bfT7LeO553TXQDvJw==", "dev": true, "requires": { "detect-libc": "^1.0.3" @@ -29309,16 +33389,16 @@ } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "dependencies": { @@ -29333,7 +33413,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -29349,12 +33429,13 @@ } }, "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "findup-sync": { @@ -29367,6 +33448,151 @@ "is-glob": "^4.0.0", "micromatch": "^3.0.4", "resolve-dir": "^1.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } } }, "fined": { @@ -29416,9 +33642,9 @@ } }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "flush-write-stream": { @@ -29432,48 +33658,51 @@ } }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true }, "for-own": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", "dev": true, "requires": { "for-in": "^1.0.1" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, "foreachasync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", - "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=", + "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==", "dev": true }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true }, "fork-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", - "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", + "integrity": "sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==", "dev": true }, "form-data": { @@ -29495,7 +33724,7 @@ "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", "dev": true, "requires": { "map-cache": "^0.2.2" @@ -29504,12 +33733,12 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", "dev": true }, "fs-constants": { @@ -29519,9 +33748,9 @@ "dev": true }, "fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "requires": { "graceful-fs": "^4.2.0", @@ -29532,7 +33761,7 @@ "fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", - "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -29554,7 +33783,7 @@ "fs.extra": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz", - "integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=", + "integrity": "sha512-Ig401VXtyrWrz23k9KxAx9OrnL8AHSLNhQ8YJH2wSYuH0ZUfxwBeY6zXkd/oOyVRFTlpEu/0n5gHeuZt7aqbkw==", "dev": true, "requires": { "fs-extra": "~0.6.1", @@ -29565,7 +33794,7 @@ "fs-extra": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", - "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", + "integrity": "sha512-5rU898vl/Z948L+kkJedbmo/iltzmiF5bn/eEk0j/SgrPpI+Ydau9xlJPicV7Av2CHYBGz5LAlwTnBU80j1zPQ==", "dev": true, "requires": { "jsonfile": "~1.0.1", @@ -29577,19 +33806,19 @@ "jsonfile": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", - "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=", + "integrity": "sha512-KbsDJNRfRPF5v49tMNf9sqyyGqGLBcz1v5kZT01kG5ns5mQSltwxCKVmUzVKtEinkUnTDtSrp6ngWpV7Xw0ZlA==", "dev": true }, "mkdirp": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", + "integrity": "sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==", "dev": true }, "rimraf": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "integrity": "sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==", "dev": true } } @@ -29597,7 +33826,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "fsevents": { @@ -29619,12 +33848,12 @@ }, "dependencies": { "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" } }, "rimraf": { @@ -29651,10 +33880,28 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, "gaze": { @@ -29680,17 +33927,17 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "get-package-type": { @@ -29727,13 +33974,13 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", "dev": true }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -29765,15 +34012,15 @@ "dev": true }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -29818,7 +34065,7 @@ "is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, "requires": { "is-extglob": "^2.1.0" @@ -29860,7 +34107,7 @@ "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, "requires": { "remove-trailing-separator": "^1.0.1" @@ -29868,6 +34115,12 @@ } } }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", @@ -29915,7 +34168,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -29924,7 +34177,7 @@ "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -29957,7 +34210,7 @@ "is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, "requires": { "is-extglob": "^2.1.0" @@ -29968,18 +34221,99 @@ "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", "dev": true, "requires": { "binary-extensions": "^1.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", @@ -29994,7 +34328,7 @@ "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", "dev": true, "requires": { "is-number": "^3.0.0", @@ -30027,7 +34361,7 @@ "global-prefix": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -30037,6 +34371,12 @@ "which": "^1.2.14" }, "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -30060,13 +34400,13 @@ "dev": true }, "globule": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", - "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", + "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", "dev": true, "requires": { "glob": "~7.1.1", - "lodash": "~4.17.10", + "lodash": "^4.17.21", "minimatch": "~3.0.2" }, "dependencies": { @@ -30124,9 +34464,9 @@ } }, "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "grapheme-splitter": { @@ -30166,56 +34506,6 @@ "vinyl": "^2.1.0" }, "dependencies": { - "arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" - } - }, - "arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", - "dev": true - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", - "dev": true - }, - "extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", - "dev": true, - "requires": { - "kind-of": "^1.1.0" - } - }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", - "dev": true - }, - "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", - "dev": true, - "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" - } - }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -30275,19 +34565,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", "dev": true }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", "dev": true, "requires": { "string-width": "^1.0.1", @@ -30295,10 +34585,16 @@ "wrap-ansi": "^2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", "dev": true, "requires": { "path-exists": "^2.0.0", @@ -30311,19 +34607,37 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", "dev": true, "requires": { "number-is-nan": "^1.0.0" } }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", "dev": true, "requires": { "pinkie-promise": "^2.0.0" @@ -30332,7 +34646,7 @@ "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", "dev": true, "requires": { "load-json-file": "^1.0.0", @@ -30343,23 +34657,23 @@ "read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", "dev": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" } }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -30370,22 +34684,16 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" } }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", "dev": true, "requires": { "string-width": "^1.0.1", @@ -30434,7 +34742,7 @@ "gulp-concat": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", - "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "integrity": "sha512-a2scActrQrDBpBbR3WUZGyGS1JEPLg5PZJdIa7/Bi3GuKAmPYDK6SFhy/NZq5R8KsKKFvtfR0fakbUCcKGCCjg==", "dev": true, "requires": { "concat-with-sourcemaps": "^1.0.0", @@ -30486,10 +34794,22 @@ "ms": "2.0.0" } }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "requires": { "depd": "~1.1.2", @@ -30501,7 +34821,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, "mime": { @@ -30513,9 +34833,18 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -30562,12 +34891,33 @@ "plugin-error": "^1.0.1" }, "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, "ansi-regex": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -30661,18 +35011,6 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -30710,6 +35048,16 @@ "eslint-visitor-keys": "^1.1.0" } }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", @@ -30745,6 +35093,12 @@ "type-fest": "^0.8.1" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "inquirer": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", @@ -30832,6 +35186,18 @@ "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -30853,15 +35219,6 @@ "glob": "^7.1.3" } }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -30989,7 +35346,7 @@ "gulp-js-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gulp-js-escape/-/gulp-js-escape-1.0.1.tgz", - "integrity": "sha1-HNRF+9AJ4Np2lZoDp/SbNWav+Gg=", + "integrity": "sha512-F+53crhLb78CTlG7ZZJFWzP0+/4q0vt2/pULXFkTMs6AGBo0Eh5cx+eWsqqHv8hrNIUsuTab3Se8rOOzP/6+EQ==", "dev": true, "requires": { "through2": "^0.6.3" @@ -30998,13 +35355,13 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -31016,13 +35373,13 @@ "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true }, "through2": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", "dev": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", @@ -31054,9 +35411,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", - "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", + "version": "14.18.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", + "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==", "dev": true } } @@ -31075,6 +35432,15 @@ "tslib": "^1.10.0" }, "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -31084,6 +35450,18 @@ "color-convert": "^2.0.1" } }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true + }, "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -31109,6 +35487,34 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -31183,6 +35589,51 @@ "terser": "^5.9.0", "through2": "^4.0.2", "vinyl-sourcemaps-apply": "^0.2.1" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + } } }, "gulp-util": { @@ -31214,19 +35665,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -31239,24 +35690,15 @@ "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true }, "clone-stats": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", "dev": true }, - "lodash.escape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", - "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", - "dev": true, - "requires": { - "lodash._root": "^3.0.0" - } - }, "lodash.template": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", @@ -31277,7 +35719,7 @@ "lodash.templatesettings": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "integrity": "sha512-TcrlEr31tDYnWkHFWDCV3dHYroKEXpJZ2YJYvJdhN+y4AkWMDZ5I4I8XDtUKqSAyG81N7w+I1mFEJtcED+tGqQ==", "dev": true, "requires": { "lodash._reinterpolate": "^3.0.0", @@ -31287,13 +35729,13 @@ "object-assign": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==", "dev": true }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -31302,7 +35744,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true }, "through2": { @@ -31318,7 +35760,7 @@ "vinyl": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "integrity": "sha512-P5zdf3WB9uzr7IFoVQ2wZTmUwHL8cMZWJGzLBNCHNZ3NB6HTMsYABtt7z8tAGIINLXyAob9B9a1yzVGMFOYKEA==", "dev": true, "requires": { "clone": "^1.0.0", @@ -31331,7 +35773,7 @@ "gulplog": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "integrity": "sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==", "dev": true, "requires": { "glogg": "^1.0.0" @@ -31370,7 +35812,7 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "dev": true }, "har-validator": { @@ -31381,20 +35823,6 @@ "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - } } }, "has": { @@ -31408,7 +35836,7 @@ "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -31417,32 +35845,40 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true } } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true }, "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "has-gulplog": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "integrity": "sha512-+F4GzLjwHNNDEAJW2DC1xXfEoPkRDmUdJ7CBYw4MpqtDwOnqdImJl7GWlpqx+Wko6//J8uKTnIe4wZSv7yCqmw==", "dev": true, "requires": { "sparkles": "^1.0.0" } }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -31460,7 +35896,7 @@ "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", "dev": true, "requires": { "get-value": "^2.0.6", @@ -31471,7 +35907,7 @@ "has-values": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", "dev": true, "requires": { "is-number": "^3.0.0", @@ -31484,10 +35920,30 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -31553,7 +36009,7 @@ "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "integrity": "sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==", "dev": true, "requires": { "os-homedir": "^1.0.0", @@ -31570,10 +36026,13 @@ } }, "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "html-escaper": { "version": "2.0.2", @@ -31594,21 +36053,21 @@ "dev": true }, "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", + "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", + "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "http-parser-js": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", - "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "dev": true }, "http-proxy": { @@ -31625,7 +36084,7 @@ "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -31644,12 +36103,12 @@ } }, "https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "requires": { - "agent-base": "5", + "agent-base": "6", "debug": "4" } }, @@ -31694,7 +36153,13 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", "dev": true }, "individual": { @@ -31706,7 +36171,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -31719,15 +36184,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", "dev": true }, "inquirer": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz", - "integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -31740,10 +36205,11 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.2.0", + "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" }, "dependencies": { "ansi-styles": { @@ -31780,6 +36246,21 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -31788,6 +36269,12 @@ "requires": { "has-flag": "^4.0.0" } + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true } } }, @@ -31820,7 +36307,7 @@ "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", "dev": true }, "ipaddr.js": { @@ -31868,7 +36355,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "is-bigint": { @@ -31906,15 +36393,15 @@ "dev": true }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "requires": { "has": "^1.0.3" } @@ -31993,7 +36480,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-finite": { @@ -32057,7 +36544,7 @@ "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", "dev": true }, "is-negative-zero": { @@ -32067,35 +36554,15 @@ "dev": true }, "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "requires": { "has-tostringtag": "^1.0.0" @@ -32141,7 +36608,7 @@ "is-running": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", - "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", + "integrity": "sha512-mjJd3PujZMl7j+D395WTIO5tU5RIDBfVSRtRR4VOJou3H66E38UjbjvDGh3slJzPuolsb+yQFqwHNNdyp5jg3w==", "dev": true }, "is-set": { @@ -32151,10 +36618,13 @@ "dev": true }, "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } }, "is-ssh": { "version": "1.4.0", @@ -32168,7 +36638,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true }, "is-string": { @@ -32190,22 +36660,22 @@ } }, "is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", "dev": true, "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", "has-tostringtag": "^1.0.0" } }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unc-path": { @@ -32226,13 +36696,13 @@ "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", "dev": true }, "is-valid-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", - "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", "dev": true }, "is-weakmap": { @@ -32282,33 +36752,33 @@ "dev": true }, "isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "istanbul": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "integrity": "sha512-nMtdn4hvK0HjUlzr1DrKSUY8ychprt8dzHOgY2KXsIhHu5PuQQEOTM27gV9Xblyon7aUH/TSFIjRHEODF/FRPg==", "dev": true, "requires": { "abbrev": "1.0.x", @@ -32330,7 +36800,7 @@ "glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", "dev": true, "requires": { "inflight": "^1.0.4", @@ -32343,28 +36813,28 @@ "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", "dev": true }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" } }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", "dev": true }, "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", "dev": true, "requires": { "has-flag": "^1.0.0" @@ -32387,6 +36857,19 @@ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, "istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -32398,6 +36881,12 @@ "supports-color": "^7.1.0" }, "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -32429,9 +36918,9 @@ } }, "istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -32500,6 +36989,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -32512,15 +37007,15 @@ } }, "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" }, "dependencies": { "ansi-styles": { @@ -32557,6 +37052,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -32569,21 +37070,21 @@ } }, "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" }, "dependencies": { "ansi-styles": { @@ -32620,6 +37121,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -32632,20 +37139,20 @@ } }, "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "stack-utils": "^2.0.2" }, "dependencies": { "ansi-styles": { @@ -32682,15 +37189,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true }, "supports-color": { "version": "7.2.0", @@ -32703,6 +37212,12 @@ } } }, + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true + }, "jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -32712,6 +37227,23 @@ "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "js-tokens": { @@ -32740,7 +37272,7 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, "jsesc": { @@ -32754,12 +37286,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -32781,13 +37307,13 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "json5": { @@ -32820,7 +37346,7 @@ "just-clone": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-1.0.2.tgz", - "integrity": "sha1-v7P672WqEqMWBYcSlFwyb9jwFDQ=" + "integrity": "sha512-p93GINPwrve0w3HUzpXmpTl7MyzzWz1B5ag44KEtq/hP1mtK8lA2b9Q0VQaPlnY87352osJcE6uBmN0e8kuFMw==" }, "just-debounce": { "version": "1.1.0", @@ -32835,9 +37361,9 @@ "dev": true }, "karma": { - "version": "6.3.17", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz", - "integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", + "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==", "dev": true, "requires": { "@colors/colors": "1.5.0", @@ -32859,20 +37385,31 @@ "qjobs": "^1.2.0", "range-parser": "^1.2.1", "rimraf": "^3.0.2", - "socket.io": "^4.2.0", + "socket.io": "^4.4.1", "source-map": "^0.6.1", "tmp": "^0.2.1", "ua-parser-js": "^0.7.30", "yargs": "^16.1.1" }, "dependencies": { + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" } }, "source-map": { @@ -32881,6 +37418,15 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -32925,14 +37471,14 @@ "karma-chai": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/karma-chai/-/karma-chai-0.1.0.tgz", - "integrity": "sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=", + "integrity": "sha512-mqKCkHwzPMhgTYca10S90aCEX9+HjVjjrBFAsw36Zj7BlQNbokXXCAe6Ji04VUMsxcY5RLP7YphpfO06XOubdg==", "dev": true, "requires": {} }, "karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", + "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", "dev": true, "requires": { "which": "^1.2.1" @@ -32961,21 +37507,6 @@ "istanbul-lib-source-maps": "^4.0.1", "istanbul-reports": "^3.0.5", "minimatch": "^3.0.4" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - } } }, "karma-coverage-istanbul-reporter": { @@ -33054,7 +37585,7 @@ "karma-es5-shim": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/karma-es5-shim/-/karma-es5-shim-0.0.4.tgz", - "integrity": "sha1-zdADM8znfC5M4D46yT8vjs0fuVI=", + "integrity": "sha512-8xU6F2/R6u6HAZ/nlyhhx3WEhj4C6hJorG7FR2REX81pgj2LSo9ADJXxCGIeXg6Qr2BGpxp4hcZcEOYGAwiumg==", "dev": true, "requires": { "es5-shim": "^4.0.5" @@ -33073,7 +37604,7 @@ "karma-ie-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-ie-launcher/-/karma-ie-launcher-1.0.0.tgz", - "integrity": "sha1-SXmGhCxJAZA0bNifVJTKmDDG1Zw=", + "integrity": "sha512-ts71ke8pHvw6qdRtq0+7VY3ANLoZuUNNkA8abRaWV13QRPNm7TtSOqyszjHUtuwOWKcsSz4tbUtrNICrQC+SXQ==", "dev": true, "requires": { "lodash": "^4.6.1" @@ -33091,7 +37622,7 @@ "karma-mocha-reporter": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz", - "integrity": "sha1-FRIAlejtgZGG5HoLAS8810GJVWA=", + "integrity": "sha512-Hr6nhkIp0GIJJrvzY8JFeHpQZNseuIakGac4bpw8K1+5F0tLb6l7uvXRa8mt2Z+NVwYgCct4QAfp2R2QP6o00w==", "dev": true, "requires": { "chalk": "^2.1.0", @@ -33108,7 +37639,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -33119,28 +37650,28 @@ "karma-opera-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-opera-launcher/-/karma-opera-launcher-1.0.0.tgz", - "integrity": "sha1-+lFihTGh0L6EstjcDX7iCfyP+Ro=", + "integrity": "sha512-rdty4FlVIowmUhPuG08TeXKHvaRxeDSzPxGIkWguCF3A32kE0uvXZ6dXW08PuaNjai8Ip3f5Pn9Pm2HlChaxCw==", "dev": true, "requires": {} }, "karma-safari-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz", - "integrity": "sha1-lpgqLMR9BmquccVTursoMZEVos4=", + "integrity": "sha512-qmypLWd6F2qrDJfAETvXDfxHvKDk+nyIjpH9xIeI3/hENr0U3nuqkxaftq73PfXZ4aOuOChA6SnLW4m4AxfRjQ==", "dev": true, "requires": {} }, "karma-script-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz", - "integrity": "sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0=", + "integrity": "sha512-5NRc8KmTBjNPE3dNfpJP90BArnBohYV4//MsLFfUA1e6N+G1/A5WuWctaFBtMQ6MWRybs/oguSej0JwDr8gInA==", "dev": true, "requires": {} }, "karma-sinon": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/karma-sinon/-/karma-sinon-1.0.5.tgz", - "integrity": "sha1-TjRD8oMP3s/2JNN0cWPxIX2qKpo=", + "integrity": "sha512-wrkyAxJmJbn75Dqy17L/8aILJWFm7znd1CE8gkyxTBFnjMSOe2XTJ3P30T8SkxWZHmoHX0SCaUJTDBEoXs25Og==", "dev": true, "requires": {} }, @@ -33156,7 +37687,7 @@ "karma-spec-reporter": { "version": "0.0.32", "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz", - "integrity": "sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo=", + "integrity": "sha512-ZXsYERZJMTNRR2F3QN11OWF5kgnT/K2dzhM+oY3CDyMrDI3TjIWqYGG7c15rR9wjmy9lvdC+CCshqn3YZqnNrA==", "dev": true, "requires": { "colors": "^1.1.2" @@ -33180,9 +37711,9 @@ "dev": true }, "keyv": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", - "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", + "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -33219,7 +37750,7 @@ "last-run": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", - "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "integrity": "sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==", "dev": true, "requires": { "default-resolution": "^2.0.0", @@ -33238,7 +37769,7 @@ "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", "dev": true, "requires": { "invert-kv": "^1.0.0" @@ -33247,13 +37778,13 @@ "lcov-parse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=", + "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", "dev": true }, "lead": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", "dev": true, "requires": { "flush-write-stream": "^1.0.2" @@ -33318,7 +37849,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -33332,7 +37863,7 @@ "listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", "dev": true }, "live-connect-js": { @@ -33389,19 +37920,29 @@ } }, "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, + "loader-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, "lodash": { @@ -33413,126 +37954,135 @@ "lodash._basecopy": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "integrity": "sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ==", "dev": true }, "lodash._basetostring": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", - "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "integrity": "sha512-mTzAr1aNAv/i7W43vOR/uD/aJ4ngbtsRaCubp2BfZhlGU/eORUjg/7F6X0orNMdv33JOrdgGybtvMN/po3EWrA==", "dev": true }, "lodash._basevalues": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", - "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "integrity": "sha512-H94wl5P13uEqlCg7OcNNhMQ8KvWSIyqXzOPusRgHC9DK3o54P6P3xtbXlVbRABG4q5gSmp7EDdJ0MSuW9HX6Mg==", "dev": true }, "lodash._getnative": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==", "dev": true }, "lodash._isiterateecall": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==", "dev": true }, "lodash._reescape": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", - "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "integrity": "sha512-Sjlavm5y+FUVIF3vF3B75GyXrzsfYV8Dlv3L4mEpuB9leg8N6yf/7rU06iLPx9fY0Mv3khVp9p7Dx0mGV6V5OQ==", "dev": true }, "lodash._reevaluate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", - "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "integrity": "sha512-OrPwdDc65iJiBeUe5n/LIjd7Viy99bKwDdk7Z5ljfZg0uFRFlfQaCy9tZ4YMAag9WAZmlVpe1iZrkIMMSMHD3w==", "dev": true }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", "dev": true }, "lodash._root": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==", "dev": true }, "lodash.clone": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", - "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", + "integrity": "sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==", "dev": true }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "dev": true }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true }, "lodash.difference": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", "dev": true }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha512-n1PZMXgaaDWZDSvuNZ/8XOcYO2hOKDqZel5adtR30VKQAtoWs/5AOeFA0vPV8moiPzlqe7F4cP2tzpFewQyelQ==", + "dev": true, + "requires": { + "lodash._root": "^3.0.0" + } + }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", "dev": true }, "lodash.isarray": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", "dev": true }, "lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", "dev": true }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, "lodash.keys": { @@ -33555,19 +38105,19 @@ "lodash.pickby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=", + "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", "dev": true }, "lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "integrity": "sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==", "dev": true }, "lodash.some": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", "dev": true }, "lodash.template": { @@ -33592,19 +38142,19 @@ "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", "dev": true }, "lodash.zip": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", - "integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", "dev": true }, "log-driver": { @@ -33623,16 +38173,16 @@ } }, "log4js": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.2.tgz", - "integrity": "sha512-k80cggS2sZQLBwllpT1p06GtfvzMmSdUCkW96f0Hj83rKGJDAu2vZjt9B9ag2vx8Zz1IXzxoLgqvRJCdMKybGg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.0.tgz", + "integrity": "sha512-KA0W9ffgNBLDj6fZCq/lRbgR6ABAodRIDHrZnS48vOtfKa4PzWImb0Md1lmGCdO3n3sbCm/n1/WmrNlZ8kCI3Q==", "dev": true, "requires": { - "date-format": "^4.0.4", - "debug": "^4.3.3", - "flatted": "^3.2.5", + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", "rfdc": "^1.3.0", - "streamroller": "^3.0.4" + "streamroller": "^3.1.3" } }, "loglevel": { @@ -33695,7 +38245,7 @@ "lru-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", "dev": true, "requires": { "es5-ext": "~0.10.2" @@ -33751,7 +38301,7 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true }, "map-obj": { @@ -33763,13 +38313,13 @@ "map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", "dev": true }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", "dev": true, "requires": { "object-visit": "^1.0.0" @@ -33782,15 +38332,15 @@ "dev": true }, "marky": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.4.tgz", - "integrity": "sha512-zd2/GiSn6U3/jeFVZ0J9CA1LzQ8RfIVvXkb/U0swFHF/zT+dVohTAWjmo2DcIuofmIIIROlwTbd+shSeXmxr0w==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", + "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", "dev": true }, "matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", - "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "integrity": "sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==", "dev": true, "requires": { "findup-sync": "^2.0.0", @@ -33799,10 +38349,90 @@ "stack-trace": "0.0.10" }, "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + } + } + }, "findup-sync": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "integrity": "sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==", "dev": true, "requires": { "detect-file": "^1.0.0", @@ -33811,14 +38441,77 @@ "resolve-dir": "^1.0.1" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, "requires": { "is-extglob": "^2.1.0" } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } } } }, @@ -33929,11 +38622,12 @@ } }, "mdast-util-gfm-table": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz", - "integrity": "sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.6.tgz", + "integrity": "sha512-uHR+fqFq3IvB3Rd4+kzXW8dmpxUhvgCQZep6KdjsLK4O6meK5dYZEayLtIxNus1XO3gfjfcIFe8a7L0HZRGgag==", "dev": true, "requires": { + "@types/mdast": "^3.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^1.0.0", "mdast-util-to-markdown": "^1.3.0" @@ -33952,24 +38646,22 @@ "mdast-util-inject": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/mdast-util-inject/-/mdast-util-inject-1.1.0.tgz", - "integrity": "sha1-2wa4tYW+lZotzS+H9HK6m3VvNnU=", + "integrity": "sha512-CcJ0mHa36QYumDKiZ2OIR+ClhfOM7zIzN+Wfy8tRZ1hpH9DKLCS+Mh4DyK5bCxzE9uxMWcbIpeNFWsg1zrj/2g==", "dev": true, "requires": { "mdast-util-to-string": "^1.0.0" } }, "mdast-util-to-hast": { - "version": "12.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.2.1.tgz", - "integrity": "sha512-dyindR2P7qOqXO1hQirZeGtVbiX7xlNQbw7gGaAwN4A1dh4+X8xU/JyYmRoyB8Fu1uPXzp7mlL5QwW7k+knvgA==", + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.2.4.tgz", + "integrity": "sha512-a21xoxSef1l8VhHxS1Dnyioz6grrJkoaCUgGzMD/7dWHvboYX3VW53esRUfB5tgTyz4Yos1n25SPcj35dJqmAg==", "dev": true, "requires": { "@types/hast": "^2.0.0", "@types/mdast": "^3.0.0", - "@types/mdurl": "^1.0.0", "mdast-util-definitions": "^5.0.0", - "mdurl": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-sanitize-uri": "^1.1.0", "trim-lines": "^3.0.0", "unist-builder": "^3.0.0", "unist-util-generated": "^2.0.0", @@ -34051,16 +38743,10 @@ } } }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memoizee": { "version": "0.4.15", @@ -34091,7 +38777,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "merge-stream": { "version": "2.0.0", @@ -34102,12 +38788,12 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "micromark": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", - "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.1.0.tgz", + "integrity": "sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==", "dev": true, "requires": { "@types/debug": "^4.0.0", @@ -34397,9 +39083,9 @@ } }, "micromark-util-sanitize-uri": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz", - "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.1.0.tgz", + "integrity": "sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -34432,100 +39118,13 @@ "dev": true }, "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "mime": { @@ -34535,16 +39134,16 @@ "dev": true }, "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -34578,9 +39177,9 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, "mixin-deep": { @@ -34634,6 +39233,12 @@ "yargs-unparser": "2.0.0" }, "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -34649,15 +39254,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -34679,6 +39275,17 @@ } } }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -34694,22 +39301,11 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true }, "escape-string-regexp": { "version": "4.0.0", @@ -34727,6 +39323,37 @@ "path-exists": "^4.0.0" } }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -34762,6 +39389,17 @@ "dev": true, "requires": { "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } } }, "ms": { @@ -34770,12 +39408,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -34794,23 +39426,20 @@ "p-limit": "^3.0.2" } }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } }, "yargs": { "version": "16.2.0", @@ -34857,17 +39486,20 @@ "ms": "2.0.0" } }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } } } }, @@ -34890,9 +39522,9 @@ "dev": true }, "mrmime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.0.tgz", - "integrity": "sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", "dev": true }, "ms": { @@ -34903,45 +39535,10 @@ "multipipe": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "integrity": "sha512-7ZxrUybYv9NonoXgwoOqtStIu18D1c3eFZj27hqgf5kBrBF8Q+tE8V0MW8dKM5QLkQPh1JhhbKgHLY9kifov4Q==", "dev": true, "requires": { "duplexer2": "0.0.2" - }, - "dependencies": { - "duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", - "dev": true, - "requires": { - "readable-stream": "~1.1.9" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } } }, "mute-stdout": { @@ -34974,11 +39571,10 @@ "optional": true }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true, - "optional": true + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true }, "nanomatch": { "version": "1.2.13", @@ -34999,6 +39595,22 @@ "to-regex": "^3.0.1" }, "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -35010,13 +39622,13 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "ncp": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", - "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", + "integrity": "sha512-PfGU8jYWdRl4FqJfCy0IzbkGyFHntfWygZg46nFk/dJD/XRrk2cj0SsKSX9n5u5gE0E0YfEpKWrEkfjnlZSTXA==", "dev": true }, "negotiator": { @@ -35068,7 +39680,7 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "lolex": { @@ -35108,29 +39720,32 @@ "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", "dev": true, "requires": { "abbrev": "1" } }, "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -35158,7 +39773,7 @@ "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "requires": { "path-key": "^2.0.0" @@ -35167,7 +39782,7 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true } } @@ -35175,7 +39790,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", "dev": true }, "oauth-sign": { @@ -35187,13 +39802,13 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", "dev": true, "requires": { "copy-descriptor": "^0.1.0", @@ -35204,7 +39819,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -35213,7 +39828,7 @@ "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -35228,7 +39843,7 @@ "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -35256,7 +39871,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -35265,10 +39880,9 @@ } }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, "object-is": { "version": "1.1.5", @@ -35283,32 +39897,34 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", "dev": true, "requires": { "isobject": "^3.0.0" } }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, "object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", "dev": true, "requires": { "array-each": "^1.0.1", @@ -35320,7 +39936,7 @@ "object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", "dev": true, "requires": { "for-own": "^1.0.0", @@ -35330,7 +39946,7 @@ "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -35339,7 +39955,7 @@ "object.reduce": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", - "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "integrity": "sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==", "dev": true, "requires": { "for-own": "^1.0.0", @@ -35358,9 +39974,9 @@ } }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } @@ -35374,7 +39990,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -35407,7 +40023,7 @@ "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", "dev": true } } @@ -35477,6 +40093,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -35501,7 +40123,7 @@ "ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==", "dev": true, "requires": { "readable-stream": "^2.0.1" @@ -35510,13 +40132,13 @@ "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true }, "os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", "dev": true, "requires": { "lcid": "^1.0.0" @@ -35525,7 +40147,7 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true }, "p-cancelable": { @@ -35537,7 +40159,7 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true }, "p-iteration": { @@ -35547,27 +40169,27 @@ "dev": true }, "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "^2.2.0" } }, "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "parent-module": { @@ -35582,7 +40204,7 @@ "parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, "requires": { "is-absolute": "^1.0.0", @@ -35617,7 +40239,7 @@ "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true }, "parse-path": { @@ -35649,25 +40271,25 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", "dev": true }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", "dev": true }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { @@ -35684,7 +40306,7 @@ "path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, "requires": { "path-root-regex": "^0.1.0" @@ -35693,13 +40315,13 @@ "path-root-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "path-type": { "version": "1.1.0", @@ -35729,7 +40351,7 @@ "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", "dev": true, "requires": { "through": "~2.3" @@ -35738,13 +40360,13 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, "picocolors": { @@ -35767,13 +40389,13 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "requires": { "pinkie": "^2.0.0" @@ -35795,98 +40417,46 @@ "dev": true, "requires": { "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } } }, "plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", "dev": true, "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - }, - "dependencies": { - "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "requires": { - "ansi-wrap": "^0.1.0" - } - } + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" } }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", "dev": true }, "postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "version": "8.4.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", + "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", "dev": true, "optional": true, "requires": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" + }, + "dependencies": { + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "optional": true + } } }, "prelude-ls": { @@ -35896,20 +40466,39 @@ "dev": true }, "pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", "dev": true, "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", "react-is": "^17.0.1" }, "dependencies": { "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true } } @@ -35917,7 +40506,7 @@ "pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", "dev": true }, "pretty-ms": { @@ -35929,12 +40518,6 @@ "parse-ms": "^2.1.0" } }, - "printj": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz", - "integrity": "sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg==", - "dev": true - }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -35989,7 +40572,7 @@ "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true }, "ps-tree": { @@ -36004,13 +40587,13 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "pump": { @@ -36065,16 +40648,16 @@ "dev": true }, "puppeteer-core": { - "version": "13.5.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.5.1.tgz", - "integrity": "sha512-dobVqWjV34ilyfQHR3BBnCYaekBYTi5MgegEYBRYd3s3uFy8jUpZEEWbaFjG9ETm+LGzR5Lmr0aF6LLuHtiuCg==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.7.0.tgz", + "integrity": "sha512-rXja4vcnAzFAP1OVLq/5dWNfwBGuzcOARJ6qGV7oAZhnLmVRU8G5MsdeQEAOy332ZhkIOnn9jp15R89LKHyp2Q==", "dev": true, "requires": { "cross-fetch": "3.1.5", - "debug": "4.3.3", - "devtools-protocol": "0.0.969999", + "debug": "4.3.4", + "devtools-protocol": "0.0.981744", "extract-zip": "2.0.1", - "https-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", "pkg-dir": "4.2.0", "progress": "2.0.3", "proxy-from-env": "1.1.0", @@ -36084,31 +40667,12 @@ "ws": "8.5.0" }, "dependencies": { - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, "devtools-protocol": { - "version": "0.0.969999", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.969999.tgz", - "integrity": "sha512-6GfzuDWU0OFAuOvBokXpXPLxjOJ5DZ157Ue3sGQQM3LgAamb8m0R0ruSfN0DDu+XG5XJgT50i6zZ/0o8RglreQ==", + "version": "0.0.981744", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.981744.tgz", + "integrity": "sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==", "dev": true }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, "ws": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", @@ -36121,7 +40685,7 @@ "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", "dev": true }, "qjobs": { @@ -36131,9 +40695,12 @@ "dev": true }, "qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } }, "query-selector-shadow-dom": { "version": "1.0.0", @@ -36144,7 +40711,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", "dev": true }, "querystringify": { @@ -36174,12 +40741,12 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { "bytes": "3.1.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } @@ -36202,36 +40769,6 @@ "type-fest": "^2.0.0" }, "dependencies": { - "hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -36326,18 +40863,44 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true } } }, "readdir-glob": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz", - "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz", + "integrity": "sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==", "dev": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "^5.1.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "readdirp": { @@ -36352,30 +40915,19 @@ "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, "requires": { "resolve": "^1.1.6" } }, "recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "dev": true, "requires": { - "minimatch": "3.0.4" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "minimatch": "^3.0.5" } }, "regenerate": { @@ -36384,23 +40936,22 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "requires": { "regenerate": "^1.4.2" } }, "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" }, "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", "requires": { "@babel/runtime": "^7.8.4" } @@ -36413,16 +40964,29 @@ "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + } } }, "regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, "regexpp": { @@ -36432,27 +40996,27 @@ "dev": true }, "regexpu-core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", - "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", + "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", "requires": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.0.0" } }, "regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" }, "regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "requires": { "jsesc": "~0.5.0" }, @@ -36460,7 +41024,7 @@ "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" } } }, @@ -36566,7 +41130,7 @@ "remove-bom-stream": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", "dev": true, "requires": { "remove-bom-buffer": "^3.0.0", @@ -36589,7 +41153,7 @@ "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true }, "repeat-element": { @@ -36601,13 +41165,13 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", "dev": true, "requires": { "is-finite": "^1.0.0" @@ -36616,13 +41180,13 @@ "replace-ext": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", "dev": true }, "replace-homedir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", - "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "integrity": "sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg==", "dev": true, "requires": { "homedir-polyfill": "^1.0.1", @@ -36680,7 +41244,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-from-string": { @@ -36689,10 +41253,16 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "resolve": { @@ -36714,7 +41284,7 @@ "resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, "requires": { "expand-tilde": "^2.0.0", @@ -36730,7 +41300,7 @@ "resolve-options": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", "dev": true, "requires": { "value-or-function": "^3.0.0" @@ -36739,13 +41309,13 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", "dev": true }, "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "requires": { "lowercase-keys": "^2.0.0" @@ -36763,7 +41333,7 @@ "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", "dev": true } } @@ -36821,20 +41391,12 @@ } }, "rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { - "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } + "tslib": "^1.9.0" } }, "sade": { @@ -36847,25 +41409,36 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safe-json-parse": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", + "integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A==", "dev": true }, "safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", "dev": true, "requires": { "ret": "~0.1.10" } }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -36899,13 +41472,6 @@ "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} } } }, @@ -36917,30 +41483,30 @@ "semver-greatest-satisfied-range": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", - "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "integrity": "sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ==", "dev": true, "requires": { "sver-compat": "^1.5.0" } }, "send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { "debug": { @@ -36954,7 +41520,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -36999,7 +41565,7 @@ "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -37020,10 +41586,16 @@ "ms": "2.0.0" } }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "requires": { "depd": "~1.1.2", @@ -37035,13 +41607,13 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "setprototypeof": { @@ -37049,24 +41621,30 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true } } }, "serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "0.18.0" } }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "set-value": { @@ -37084,7 +41662,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -37093,7 +41671,7 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true }, "is-plain-object": { @@ -37110,7 +41688,7 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true }, "setprototypeof": { @@ -37137,7 +41715,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -37170,21 +41747,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -37200,9 +41762,9 @@ } }, "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", "dev": true }, "slice-ansi": { @@ -37270,7 +41832,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -37279,7 +41841,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -37288,7 +41850,7 @@ "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -37297,7 +41859,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -37314,7 +41876,7 @@ "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -37323,7 +41885,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -37345,13 +41907,13 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "source-map-resolve": { @@ -37383,7 +41945,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, "requires": { "is-descriptor": "^1.0.0" @@ -37409,7 +41971,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -37418,33 +41980,32 @@ } }, "socket.io": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", - "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", + "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", "dev": true, "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.0" } }, "socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", "dev": true }, "socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", "dev": true, "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, @@ -37457,7 +42018,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true }, "source-map-js": { @@ -37478,21 +42039,12 @@ } }, "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "source-map": "^0.5.6" } }, "source-map-url": { @@ -37547,15 +42099,15 @@ } }, "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, "split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", "dev": true, "requires": { "through": "2" @@ -37568,18 +42120,46 @@ "dev": true, "requires": { "extend-shallow": "^3.0.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + } } }, "split2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", - "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", - "dev": true + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "requires": { + "readable-stream": "^3.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "sshpk": { @@ -37602,7 +42182,7 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "dev": true }, "stack-utils": { @@ -37625,7 +42205,7 @@ "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", "dev": true, "requires": { "define-property": "^0.2.5", @@ -37635,7 +42215,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -37644,7 +42224,7 @@ "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -37653,7 +42233,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -37670,7 +42250,7 @@ "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -37679,7 +42259,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -37701,9 +42281,9 @@ } }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "stream-buffers": { "version": "3.0.2", @@ -37714,7 +42294,7 @@ "stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", "dev": true, "requires": { "duplexer": "~0.1.1" @@ -37733,14 +42313,42 @@ "dev": true }, "streamroller": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.4.tgz", - "integrity": "sha512-GI9NzeD+D88UFuIlJkKNDH/IsuR+qIN7Qh8EsmhoRZr9bQoehTraRgwtLUkZbpcAw+hLPfHOypmppz8YyGK68w==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.3.tgz", + "integrity": "sha512-CphIJyFx2SALGHeINanjFRKQ4l7x2c+rXYJ4BMq0gd+ZK0gi4VT8b+eHe2wi58x4UayBAKx4xtHpXT/ea1cz8w==", "dev": true, "requires": { - "date-format": "^4.0.4", - "debug": "^4.3.3", - "fs-extra": "^10.0.1" + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } } }, "string_decoder": { @@ -37750,12 +42358,20 @@ "dev": true, "requires": { "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "string-template": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", + "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==", "dev": true }, "string-width": { @@ -37767,34 +42383,28 @@ "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - } } }, "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "stringify-entities": { @@ -37819,34 +42429,39 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", "dev": true }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true + }, + "strip-json-comments": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.0.tgz", + "integrity": "sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==", "dev": true }, "suffix": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/suffix/-/suffix-0.1.1.tgz", - "integrity": "sha1-zFgjFkag7xEC95R47zqSSP2chC8=", + "integrity": "sha512-j5uf6MJtMCfC4vBe5LFktSe4bGyNTBk7I2Kdri0jeLrcv5B9pWfxVa5JQpoxgtR8vaVB7bVxsWgnfQbX5wkhAA==", "dev": true }, "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "requires": { - "has-flag": "^4.0.0" + "has-flag": "^3.0.0" } }, "supports-preserve-symlinks-flag": { @@ -37857,7 +42472,7 @@ "sver-compat": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", - "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "integrity": "sha512-aFTHfmjwizMNlNE6dsGmoAM4lHjL0CyiobWaFiXWSlD7cIxshW422Nb8KbXCmR6z+0ZEPY+daXJrDyh/vuwTyg==", "dev": true, "requires": { "es6-iterator": "^2.0.1", @@ -37878,9 +42493,9 @@ }, "dependencies": { "ajv": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", - "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -37944,7 +42559,7 @@ "temp-fs": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", - "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", + "integrity": "sha512-WfecDCR1xC9b0nsrzSaxPf3ZuWeWLUWblW4vlDQAa1biQaKHiImHnJfeQocQe/hXKMcolRzgkcVX/7kK4zoWbw==", "dev": true, "requires": { "rimraf": "~2.5.2" @@ -37953,7 +42568,7 @@ "rimraf": { "version": "2.5.4", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "integrity": "sha512-Lw7SHMjssciQb/rRz7JyPIy9+bbUshEucPoLRvWqy09vC5zQixl8Uet+Zl+SROBB/JMWHJRdCk1qdxNWHNMvlQ==", "dev": true, "requires": { "glob": "^7.0.5" @@ -37986,9 +42601,9 @@ } }, "terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", + "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", "dev": true, "requires": { "@jridgewell/source-map": "^0.3.2", @@ -37998,30 +42613,40 @@ }, "dependencies": { "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } } } }, "terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "requires": { + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "dependencies": { "ajv": { @@ -38036,13 +42661,6 @@ "uri-js": "^4.2.2" } }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -38053,12 +42671,6 @@ "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -38076,7 +42688,7 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "textextensions": { @@ -38088,7 +42700,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "through2": { @@ -38138,7 +42750,7 @@ "time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", "dev": true }, "timers-ext": { @@ -38182,18 +42794,18 @@ } }, "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "rimraf": "^3.0.0" + "os-tmpdir": "~1.0.2" } }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", "dev": true, "requires": { "is-absolute": "^1.0.0", @@ -38203,12 +42815,12 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -38223,7 +42835,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -38241,6 +42853,18 @@ "extend-shallow": "^3.0.2", "regex-not": "^1.0.2", "safe-regex": "^1.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + } } }, "to-regex-range": { @@ -38250,20 +42874,12 @@ "dev": true, "requires": { "is-number": "^7.0.0" - }, - "dependencies": { - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - } } }, "to-through": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", - "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", "dev": true, "requires": { "through2": "^2.0.3" @@ -38305,13 +42921,13 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, "traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", "dev": true }, "trim-lines": { @@ -38323,7 +42939,7 @@ "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", "dev": true }, "trough": { @@ -38333,14 +42949,14 @@ "dev": true }, "tsconfig-paths": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", - "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "requires": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" }, "dependencies": { @@ -38364,7 +42980,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -38373,7 +42989,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "type": { @@ -38415,7 +43031,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, "typescript": { @@ -38447,27 +43063,27 @@ } }, "ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "version": "0.7.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", + "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==", "dev": true }, "uglify-js": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.2.tgz", - "integrity": "sha512-peeoTk3hSwYdoc9nrdiEJk+gx1ALCtTjdYuKSXMTDqq7n1W7dHPqWDdSi+BPL0ni2YMeHD7hKUSdbj3TZauY2A==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, @@ -38484,7 +43100,7 @@ "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true }, "undertaker": { @@ -38508,7 +43124,7 @@ "fast-levenshtein": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", - "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", + "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", "dev": true } } @@ -38516,7 +43132,7 @@ "undertaker-registry": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", + "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", "dev": true }, "unicode-canonical-property-names-ecmascript": { @@ -38539,9 +43155,9 @@ "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" }, "unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" }, "unified": { "version": "10.1.2", @@ -38570,10 +43186,16 @@ "set-value": "^2.0.1" }, "dependencies": { + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true } } @@ -38657,12 +43279,12 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", "dev": true, "requires": { "has-value": "^0.3.1", @@ -38672,7 +43294,7 @@ "has-value": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", "dev": true, "requires": { "get-value": "^2.0.3", @@ -38683,7 +43305,7 @@ "isobject": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", "dev": true, "requires": { "isarray": "1.0.0" @@ -38694,13 +43316,13 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", "dev": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true } } @@ -38720,6 +43342,17 @@ "listenercount": "~1.0.1", "readable-stream": "~2.3.6", "setimmediate": "~1.0.4" + }, + "dependencies": { + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + } } }, "upath": { @@ -38729,9 +43362,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.7.tgz", - "integrity": "sha512-iN/XYesmZ2RmmWAiI4Z5rq0YqSiv0brj9Ce9CfhNE4xIW2h+MFxcgkxIzZ+ShkFPUkjU3gQ+3oypadD3RAMtrg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -38749,13 +43382,13 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", "dev": true }, "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", "dev": true, "requires": { "punycode": "1.3.2", @@ -38765,7 +43398,7 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", "dev": true } } @@ -38793,29 +43426,28 @@ "dev": true }, "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dev": true, "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", "which-typed-array": "^1.1.2" } }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "3.4.0", @@ -38863,18 +43495,18 @@ "value-or-function": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", - "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", "dev": true }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -38885,15 +43517,15 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true } } }, "vfile": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.4.tgz", - "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.5.tgz", + "integrity": "sha512-U1ho2ga33eZ8y8pkbQLH54uKqGhFJ6GYIHnnG5AhRpAh3OWjkrRHKa/KogbmQn8We+c0KVV3rTOgR9V/WowbXQ==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -38932,6 +43564,12 @@ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, "string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -38979,13 +43617,13 @@ } }, "video.js": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.20.2.tgz", - "integrity": "sha512-hdvAHKAyaL6bCDkeu0pPtFYKi1EDaOUovm7FN1xqBDolUxgH8FKy1WIgTS+Ouuaw7R54SCTcSeXjZEizhy9ouQ==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.20.3.tgz", + "integrity": "sha512-JMspxaK74LdfWcv69XWhX4rILywz/eInOVPdKefpQiZJSMD5O8xXYueqACP2Q5yqKstycgmmEKlJzZ+kVmDciw==", "dev": true, "requires": { "@babel/runtime": "^7.12.5", - "@videojs/http-streaming": "2.14.2", + "@videojs/http-streaming": "2.14.3", "@videojs/vhs-utils": "^3.0.4", "@videojs/xhr": "2.6.0", "aes-decrypter": "3.1.3", @@ -38996,7 +43634,7 @@ "mux.js": "6.0.1", "safe-json-parse": "4.0.0", "videojs-font": "3.2.0", - "videojs-vtt.js": "^0.15.3" + "videojs-vtt.js": "^0.15.4" }, "dependencies": { "safe-json-parse": { @@ -39121,7 +43759,7 @@ "vinyl-sourcemap": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", - "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", "dev": true, "requires": { "append-buffer": "^1.0.2", @@ -39136,7 +43774,7 @@ "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, "requires": { "remove-trailing-separator": "^1.0.1" @@ -39147,7 +43785,7 @@ "vinyl-sourcemaps-apply": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", "dev": true, "requires": { "source-map": "^0.5.1" @@ -39156,13 +43794,13 @@ "void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", "dev": true }, "vue-template-compiler": { - "version": "2.7.10", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.10.tgz", - "integrity": "sha512-QO+8R9YRq1Gudm8ZMdo/lImZLJVUIAM8c07Vp84ojdDAf8HmPJc7XB556PcXV218k2AkKznsRz6xB5uOjAC4EQ==", + "version": "2.7.13", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.13.tgz", + "integrity": "sha512-jYM6TClwDS9YqP48gYrtAtaOhRKkbYmbzE+Q51gX5YDr777n7tNI/IZk4QV4l/PjQPNh/FVa/E92sh/RqKMrog==", "dev": true, "optional": true, "requires": { @@ -39180,9 +43818,9 @@ } }, "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -39192,51 +43830,120 @@ "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, "requires": { "defaults": "^1.0.3" } }, "webdriver": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.16.16.tgz", - "integrity": "sha512-x8UoG9k/P8KDrfSh1pOyNevt9tns3zexoMxp9cKnyA/7HYSErhZYTLGlgxscAXLtQG41cMH/Ba/oBmOx7Hgd8w==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.5.3.tgz", + "integrity": "sha512-cDTn/hYj5x8BYwXxVb/WUwqGxrhCMP2rC8ttIWCfzmiVtmOnJGulC7CyxU3+p9Q5R/gIKTzdJOss16dhb+5CoA==", "dev": true, "requires": { - "@types/node": "^17.0.4", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@wdio/config": "7.5.3", + "@wdio/logger": "7.5.3", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.5.3", + "@wdio/utils": "7.5.3", "got": "^11.0.2", - "ky": "^0.29.0", "lodash.merge": "^4.6.1" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", + "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", + "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "dev": true, + "requires": { + "got": "^11.8.1" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "webdriverio": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.16.16.tgz", - "integrity": "sha512-caPaEWyuD3Qoa7YkW4xCCQA4v9Pa9wmhFGPvNZh3ERtjMCNi8L/XXOdkekWNZmFh3tY0kFguBj7+fAwSY7HAGw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.25.4.tgz", + "integrity": "sha512-agkgwn2SIk5cAJ03uue1GnGZcUZUDN3W4fUMY9/VfO8bVJrPEgWg31bPguEWPu+YhEB/aBJD8ECxJ3OEKdy4qQ==", "dev": true, "requires": { "@types/aria-query": "^5.0.0", - "@types/node": "^17.0.4", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/repl": "7.16.14", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", + "@types/node": "^18.0.0", + "@wdio/config": "7.25.4", + "@wdio/logger": "7.19.0", + "@wdio/protocols": "7.22.0", + "@wdio/repl": "7.25.4", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", "archiver": "^5.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools": "7.16.16", - "devtools-protocol": "^0.0.973690", + "devtools": "7.25.4", + "devtools-protocol": "^0.0.1061995", "fs-extra": "^10.0.0", - "get-port": "^5.1.1", "grapheme-splitter": "^1.0.2", "lodash.clonedeep": "^4.5.0", "lodash.isobject": "^3.0.2", @@ -39248,9 +43955,85 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^8.0.0", - "webdriver": "7.16.16" + "webdriver": "7.25.4" }, "dependencies": { + "@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "dev": true + }, + "@wdio/config": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.25.4.tgz", + "integrity": "sha512-vb0emDtD9FbFh/yqW6oNdo2iuhQp8XKj6GX9fyy9v4wZgg3B0HPMVJxhIfcoHz7LMBWlHSo9YdvhFI5EQHRLBA==", + "dev": true, + "requires": { + "@wdio/logger": "7.19.0", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", + "deepmerge": "^4.0.0", + "glob": "^8.0.3" + } + }, + "@wdio/logger": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.19.0.tgz", + "integrity": "sha512-xR7SN/kGei1QJD1aagzxs3KMuzNxdT/7LYYx+lt6BII49+fqL/SO+5X0FDCZD0Ds93AuQvvz9eGyzrBI2FFXmQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/protocols": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.22.0.tgz", + "integrity": "sha512-8EXRR+Ymdwousm/VGtW3H1hwxZ/1g1H99A1lF0U4GuJ5cFWHCd0IVE5H31Z52i8ZruouW8jueMkGZPSo2IIUSQ==", + "dev": true + }, + "@wdio/repl": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.25.4.tgz", + "integrity": "sha512-kYhj9gLsUk4HmlXLqkVre+gwbfvw9CcnrHjqIjrmMS4mR9D8zvBb5CItb3ZExfPf9jpFzIFREbCAYoE9x/kMwg==", + "dev": true, + "requires": { + "@wdio/utils": "7.25.4" + } + }, + "@wdio/types": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.25.4.tgz", + "integrity": "sha512-muvNmq48QZCvocctnbe0URq2FjJjUPIG4iLoeMmyF0AQgdbjaUkMkw3BHYNHVTbSOU9WMsr2z8alhj/I2H6NRQ==", + "dev": true, + "requires": { + "@types/node": "^18.0.0", + "got": "^11.8.1" + } + }, + "@wdio/utils": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.25.4.tgz", + "integrity": "sha512-8iwQDk+foUqSzKZKfhLxjlCKOkfRJPNHaezQoevNgnrTq/t0ek+ldZCATezb9B8jprAuP4mgS9xi22akc6RkzA==", + "dev": true, + "requires": { + "@wdio/logger": "7.19.0", + "@wdio/types": "7.25.4", + "p-iteration": "^1.1.8" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -39260,6 +44043,56 @@ "balanced-match": "^1.0.0" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ky": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.30.0.tgz", + "integrity": "sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog==", + "dev": true + }, "minimatch": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", @@ -39268,19 +44101,45 @@ "requires": { "brace-expansion": "^2.0.1" } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "webdriver": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.25.4.tgz", + "integrity": "sha512-6nVDwenh0bxbtUkHASz9B8T9mB531Fn1PcQjUGj2t5dolLPn6zuK1D7XYVX40hpn6r3SlYzcZnEBs4X0az5Txg==", + "dev": true, + "requires": { + "@types/node": "^18.0.0", + "@wdio/config": "7.25.4", + "@wdio/logger": "7.19.0", + "@wdio/protocols": "7.22.0", + "@wdio/types": "7.25.4", + "@wdio/utils": "7.25.4", + "got": "^11.0.2", + "ky": "0.30.0", + "lodash.merge": "^4.6.1" + } } } }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, "webpack": { - "version": "5.70.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz", - "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==", + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", @@ -39288,31 +44147,31 @@ "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", + "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.9.2", + "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "dependencies": { "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, "acorn-import-assertions": { @@ -39334,13 +44193,6 @@ "uri-js": "^4.2.2" } }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -39355,9 +44207,9 @@ } }, "webpack-bundle-analyzer": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz", - "integrity": "sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz", + "integrity": "sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg==", "dev": true, "requires": { "acorn": "^8.0.4", @@ -39372,15 +44224,9 @@ }, "dependencies": { "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, "ansi-styles": { @@ -39423,6 +44269,12 @@ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -39433,9 +44285,9 @@ } }, "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "dev": true, "requires": {} } @@ -39498,6 +44350,66 @@ "supports-color": "^8.1.1", "through": "^2.3.8", "vinyl": "^2.2.1" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "websocket-driver": { @@ -39520,7 +44432,7 @@ "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "requires": { "tr46": "~0.0.3", @@ -39561,18 +44473,66 @@ "is-weakset": "^2.0.1" } }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true + }, "which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", "dev": true, "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "is-typed-array": "^1.1.9" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "word-wrap": { @@ -39584,13 +44544,13 @@ "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "wrap-ansi": { @@ -39633,7 +44593,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "write": { @@ -39684,13 +44644,13 @@ "yargs": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.3.tgz", - "integrity": "sha1-BU3oth8i7v23IHBZ6u+da4P7kxo=", + "integrity": "sha512-7OGt4xXoWJQh5ulgZ78rKaqY7dNWbjfK+UKxGcIlaM2j7C4fqGchyv8CPvEWdRPrHp6Ula/YU8yGRpYGOHrI+g==", "dev": true }, "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yargs-unparser": { @@ -39711,12 +44671,6 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -39728,7 +44682,7 @@ "yarn-install": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/yarn-install/-/yarn-install-1.0.0.tgz", - "integrity": "sha1-V/RQULgu/VcYKzlzxUqgXLXSUjA=", + "integrity": "sha512-VO1u181msinhPcGvQTVMnHVOae8zjX/NSksR17e6eXHRveDvHCF5mGjh9hkN8mzyfnCqcBe42LdTs7bScuTaeg==", "dev": true, "requires": { "cac": "^3.0.3", @@ -39739,19 +44693,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -39764,7 +44718,7 @@ "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -39784,7 +44738,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -39793,7 +44747,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true }, "which": { @@ -39808,7 +44762,7 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true } } @@ -39816,7 +44770,7 @@ "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "requires": { "buffer-crc32": "~0.2.3", diff --git a/package.json b/package.json index 60586841a53..21b0f56d38f 100644 --- a/package.json +++ b/package.json @@ -35,13 +35,13 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", - "@wdio/browserstack-service": "^7.16.0", - "@wdio/cli": "^7.5.2", - "@wdio/concise-reporter": "^7.5.2", - "@wdio/local-runner": "^7.5.2", - "@wdio/mocha-framework": "^7.5.2", - "@wdio/spec-reporter": "^7.19.0", - "@wdio/sync": "^7.5.2", + "@wdio/browserstack-service": "~7.16.0", + "@wdio/cli": "~7.5.2", + "@wdio/concise-reporter": "~7.5.2", + "@wdio/local-runner": "~7.5.2", + "@wdio/mocha-framework": "~7.5.2", + "@wdio/spec-reporter": "~7.19.0", + "@wdio/sync": "~7.5.2", "ajv": "6.12.3", "assert": "^2.0.0", "babel-loader": "^8.0.5", From bf85447707945290055a5734665666e5e5099979 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 3 Nov 2022 12:39:08 -0700 Subject: [PATCH 410/569] Prebid core: fix exception in `requestBids` introduced by #9106 (#9194) * Revert "Prebid core: return a promise from `requestBids` (#9106)" This reverts commit 64aff9b1af9bb1b2761e1012f628299f16c0eb46. * Revert "Revert "Prebid core: return a promise from `requestBids` (#9106)"" This reverts commit bf5ee3015059fd80a415ec67e7c5f690076a2ae1. * Prebid core: fix exception in `requestBids` introduced by #9106 * Add comments --- src/prebid.js | 162 ++++++++++++++++---------------- test/spec/unit/pbjs_api_spec.js | 18 +++- 2 files changed, 100 insertions(+), 80 deletions(-) diff --git a/src/prebid.js b/src/prebid.js index 94391343cd3..06429b13a72 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -49,6 +49,7 @@ import {default as adapterManager, gdprDataHandler, getS2SBidderSet, uspDataHand import CONSTANTS from './constants.json'; import * as events from './events.js'; import {newMetrics, useMetrics} from './utils/perfMetrics.js'; +import {defer} from './utils/promise.js'; const $$PREBID_GLOBAL$$ = getGlobal(); const { triggerUserSyncs } = userSync; @@ -622,7 +623,7 @@ $$PREBID_GLOBAL$$.removeAdUnit = function (adUnitCode) { * @alias module:pbjs.requestBids */ $$PREBID_GLOBAL$$.requestBids = (function() { - const delegate = hook('sync', function ({ bidsBackHandler, timeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2, metrics } = {}) { + const delegate = hook('async', function ({ bidsBackHandler, timeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2, metrics, defer } = {}) { events.emit(REQUEST_BIDS); const cbTimeout = timeout || config.getConfig('bidderTimeout'); logInfo('Invoking $$PREBID_GLOBAL$$.requestBids', arguments); @@ -630,23 +631,28 @@ $$PREBID_GLOBAL$$.requestBids = (function() { global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, cfg.ortb2]).filter(([_, ortb2]) => ortb2 != null)) } - return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2Fragments, metrics}); + return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2Fragments, metrics, defer}); }, 'requestBids'); return wrapHook(delegate, function requestBids(req = {}) { - // if the request does not specify adUnits, clone the global adUnit array - before - // any hook has a chance to run. + // unlike the main body of `delegate`, this runs before any other hook has a chance to; + // it's also not restricted in its return value in the way `async` hooks are. + + // if the request does not specify adUnits, clone the global adUnit array; // otherwise, if the caller goes on to use addAdUnits/removeAdUnits, any asynchronous logic // in any hook might see their effects. - req.metrics = newMetrics(); - req.metrics.checkpoint('requestBids'); let adUnits = req.adUnits || $$PREBID_GLOBAL$$.adUnits; req.adUnits = (isArray(adUnits) ? adUnits.slice() : [adUnits]); - return delegate.call(this, req); + + req.metrics = newMetrics(); + req.metrics.checkpoint('requestBids'); + req.defer = defer({promiseFactory: (r) => new Promise(r)}) + delegate.call(this, req); + return req.defer.promise; }); })(); -export const startAuction = hook('sync', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, ttlBuffer, adUnitCodes, labels, auctionId, ortb2Fragments, metrics } = {}) { +export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, ttlBuffer, adUnitCodes, labels, auctionId, ortb2Fragments, metrics, defer } = {}) { const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []); adUnits = useMetrics(metrics).measureTime('requestBids.validate', () => checkAdUnitSetup(adUnits)); @@ -658,85 +664,83 @@ export const startAuction = hook('sync', function ({ bidsBackHandler, timeout: c adUnitCodes = adUnits && adUnits.map(unit => unit.code); } - return new Promise((resolve) => { - function auctionDone(bids, timedOut, auctionId) { - if (typeof bidsBackHandler === 'function') { - try { - bidsBackHandler(bids, timedOut, auctionId); - } catch (e) { - logError('Error executing bidsBackHandler', null, e); - } + function auctionDone(bids, timedOut, auctionId) { + if (typeof bidsBackHandler === 'function') { + try { + bidsBackHandler(bids, timedOut, auctionId); + } catch (e) { + logError('Error executing bidsBackHandler', null, e); } - resolve({bids, timedOut, auctionId}); } + defer.resolve({bids, timedOut, auctionId}) + } + + /* + * for a given adunit which supports a set of mediaTypes + * and a given bidder which supports a set of mediaTypes + * a bidder is eligible to participate on the adunit + * if it supports at least one of the mediaTypes on the adunit + */ + adUnits.forEach(adUnit => { + // get the adunit's mediaTypes, defaulting to banner if mediaTypes isn't present + const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || { 'banner': 'banner' }); - /* - * for a given adunit which supports a set of mediaTypes - * and a given bidder which supports a set of mediaTypes - * a bidder is eligible to participate on the adunit - * if it supports at least one of the mediaTypes on the adunit - */ - adUnits.forEach(adUnit => { - // get the adunit's mediaTypes, defaulting to banner if mediaTypes isn't present - const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || { 'banner': 'banner' }); - - // get the bidder's mediaTypes - const allBidders = adUnit.bids.map(bid => bid.bidder); - const bidderRegistry = adapterManager.bidderRegistry; - - const bidders = allBidders.filter(bidder => !s2sBidders.has(bidder)); - - const tid = adUnit.ortb2Imp?.ext?.tid || generateUUID(); - adUnit.transactionId = tid; - if (ttlBuffer != null && !adUnit.hasOwnProperty('ttlBuffer')) { - adUnit.ttlBuffer = ttlBuffer; + // get the bidder's mediaTypes + const allBidders = adUnit.bids.map(bid => bid.bidder); + const bidderRegistry = adapterManager.bidderRegistry; + + const bidders = allBidders.filter(bidder => !s2sBidders.has(bidder)); + + const tid = adUnit.ortb2Imp?.ext?.tid || generateUUID(); + adUnit.transactionId = tid; + if (ttlBuffer != null && !adUnit.hasOwnProperty('ttlBuffer')) { + adUnit.ttlBuffer = ttlBuffer; + } + // Populate ortb2Imp.ext.tid with transactionId. Specifying a transaction ID per item in the ortb impression array, lets multiple transaction IDs be transmitted in a single bid request. + deepSetValue(adUnit, 'ortb2Imp.ext.tid', tid); + + bidders.forEach(bidder => { + const adapter = bidderRegistry[bidder]; + const spec = adapter && adapter.getSpec && adapter.getSpec(); + // banner is default if not specified in spec + const bidderMediaTypes = (spec && spec.supportedMediaTypes) || ['banner']; + + // check if the bidder's mediaTypes are not in the adUnit's mediaTypes + const bidderEligible = adUnitMediaTypes.some(type => includes(bidderMediaTypes, type)); + if (!bidderEligible) { + // drop the bidder from the ad unit if it's not compatible + logWarn(unsupportedBidderMessage(adUnit, bidder)); + adUnit.bids = adUnit.bids.filter(bid => bid.bidder !== bidder); + } else { + adunitCounter.incrementBidderRequestsCounter(adUnit.code, bidder); } - // Populate ortb2Imp.ext.tid with transactionId. Specifying a transaction ID per item in the ortb impression array, lets multiple transaction IDs be transmitted in a single bid request. - deepSetValue(adUnit, 'ortb2Imp.ext.tid', tid); - - bidders.forEach(bidder => { - const adapter = bidderRegistry[bidder]; - const spec = adapter && adapter.getSpec && adapter.getSpec(); - // banner is default if not specified in spec - const bidderMediaTypes = (spec && spec.supportedMediaTypes) || ['banner']; - - // check if the bidder's mediaTypes are not in the adUnit's mediaTypes - const bidderEligible = adUnitMediaTypes.some(type => includes(bidderMediaTypes, type)); - if (!bidderEligible) { - // drop the bidder from the ad unit if it's not compatible - logWarn(unsupportedBidderMessage(adUnit, bidder)); - adUnit.bids = adUnit.bids.filter(bid => bid.bidder !== bidder); - } else { - adunitCounter.incrementBidderRequestsCounter(adUnit.code, bidder); - } - }); - adunitCounter.incrementRequestsCounter(adUnit.code); }); + adunitCounter.incrementRequestsCounter(adUnit.code); + }); - if (!adUnits || adUnits.length === 0) { - logMessage('No adUnits configured. No bids requested.'); - auctionDone(); - } else { - const auction = auctionManager.createAuction({ - adUnits, - adUnitCodes, - callback: auctionDone, - cbTimeout, - labels, - auctionId, - ortb2Fragments, - metrics, - }); - - let adUnitsLen = adUnits.length; - if (adUnitsLen > 15) { - logInfo(`Current auction ${auction.getAuctionId()} contains ${adUnitsLen} adUnits.`, adUnits); - } + if (!adUnits || adUnits.length === 0) { + logMessage('No adUnits configured. No bids requested.'); + auctionDone(); + } else { + const auction = auctionManager.createAuction({ + adUnits, + adUnitCodes, + callback: auctionDone, + cbTimeout, + labels, + auctionId, + ortb2Fragments, + metrics, + }); - adUnitCodes.forEach(code => targeting.setLatestAuctionForAdUnit(code, auction.getAuctionId())); - auction.callBids(); + let adUnitsLen = adUnits.length; + if (adUnitsLen > 15) { + logInfo(`Current auction ${auction.getAuctionId()} contains ${adUnitsLen} adUnits.`, adUnits); } - }); + + adUnitCodes.forEach(code => targeting.setLatestAuctionForAdUnit(code, auction.getAuctionId())); + auction.callBids(); + } }, 'startAuction'); export function executeCallbacks(fn, reqBidsConfigObj) { diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index b8b82f7ca96..8e3314bd954 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1615,6 +1615,19 @@ describe('Unit: Prebid Module', function () { }); describe('returns a promise that resolves', () => { + function delayHook(next, ...args) { + setTimeout(() => next(...args)) + } + + beforeEach(() => { + // make sure the return value works correctly when hooks give up priority + $$PREBID_GLOBAL$$.requestBids.before(delayHook) + }); + + afterEach(() => { + $$PREBID_GLOBAL$$.requestBids.getHooks({hook: delayHook}).remove(); + }); + Object.entries({ 'immediately, without bidsBackHandler': (req) => $$PREBID_GLOBAL$$.requestBids(req), 'after bidsBackHandler': (() => { @@ -1665,7 +1678,10 @@ describe('Unit: Prebid Module', function () { sinon.assert.match(bids[bid.adUnitCode].bids[0], bid) done(); }); - completeAuction([bid]); + // `completeAuction` won't work until we're out of `delayHook` + // and the mocked auction has been set up; + // setTimeout here takes us after the setTimeout in `delayHook` + setTimeout(() => completeAuction([bid])); }) }) }) From 0c5450b745454d04de96881f48fb0150a9cedc63 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 3 Nov 2022 19:55:36 +0000 Subject: [PATCH 411/569] Prebid 7.24.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7567271b07f..89f17ef1a63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.24.0-pre", + "version": "7.24.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 21b0f56d38f..75fffaa4cf1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.24.0-pre", + "version": "7.24.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From abf4688ce2005f342db86d7fe8e89c2fff05b518 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 3 Nov 2022 19:55:36 +0000 Subject: [PATCH 412/569] Increment version to 7.25.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89f17ef1a63..48ef496ee8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.24.0", + "version": "7.25.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 75fffaa4cf1..d98f313f4dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.24.0", + "version": "7.25.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 7487bfd79979b0fc10e5b39fe18f1b3fb9bd6861 Mon Sep 17 00:00:00 2001 From: Jeremy Sadwith Date: Fri, 4 Nov 2022 11:09:47 -0400 Subject: [PATCH 413/569] Kargo Bid Adapter : add support for client hints (#9184) * pageURL pull from topmostLocation * Kargo: Support for client hints (#9) * Starting SUA support * Kargo: Adding support for client hints * Adding tests for sua --- modules/kargoBidAdapter.js | 22 ++++--- test/spec/modules/kargoBidAdapter_spec.js | 80 +++++++++++++++++++++-- 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index c0faf2490cb..15d706c0410 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -1,4 +1,4 @@ -import { _each, buildUrl, triggerPixel } from '../src/utils.js'; +import { _each, buildUrl, deepAccess, pick, triggerPixel } from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -37,10 +37,9 @@ export const spec = { bidSizes[bid.bidId] = bid.sizes; }); - let tdid; - if (validBidRequests.length > 0 && validBidRequests[0].userId && validBidRequests[0].userId.tdid) { - tdid = validBidRequests[0].userId.tdid; - } + const firstBidRequest = validBidRequests[0]; + + const tdid = deepAccess(firstBidRequest, 'userId.tdid') const transformedParams = Object.assign({}, { sessionId: spec._getSessionId(), @@ -62,10 +61,17 @@ export const spec = { prebidRawBidRequests: validBidRequests }, spec._getAllMetadata(bidderRequest, tdid)); + // User Agent Client Hints / SUA + const uaClientHints = deepAccess(firstBidRequest, 'ortb2.device.sua'); + if (uaClientHints) { + transformedParams.device.sua = pick(uaClientHints, ['browsers', 'platform', 'mobile', 'model']); + } + // Pull Social Canvas segments and embed URL - if (validBidRequests.length > 0 && validBidRequests[0].params.socialCanvas) { - transformedParams.socialCanvasSegments = validBidRequests[0].params.socialCanvas.segments; - transformedParams.socialEmbedURL = validBidRequests[0].params.socialCanvas.embedURL; + const socialCanvas = deepAccess(firstBidRequest, 'params.socialCanvas'); + if (socialCanvas) { + transformedParams.socialCanvasSegments = socialCanvas.segments; + transformedParams.socialEmbedURL = socialCanvas.embedURL; } const encodedParams = encodeURIComponent(JSON.stringify(transformedParams)); diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index 169ee520f33..565b83704fa 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -69,7 +69,33 @@ describe('kargo adapter tests', function () { userId: { tdid: 'fake-tdid' }, - sizes: [[320, 50], [300, 250], [300, 600]] + sizes: [[320, 50], [300, 250], [300, 600]], + ortb2: { + device: { + sua: { + platform: { + brand: 'macOS', + version: [ '12', '6', '0' ] + }, + browsers: [ + { + brand: 'Chromium', + version: [ '106', '0', '5249', '119' ] + }, + { + brand: 'Google Chrome', + version: [ '106', '0', '5249', '119' ] + }, + { + brand: 'Not;A=Brand', + version: [ '99', '0', '0', '0' ] + } + ], + mobile: 0, + model: '' + } + } + } }, { params: { @@ -259,6 +285,28 @@ describe('kargo adapter tests', function () { device: { width: screen.width, height: screen.height, + sua: { + platform: { + brand: 'macOS', + version: [ '12', '6', '0' ] + }, + browsers: [ + { + brand: 'Chromium', + version: [ '106', '0', '5249', '119' ] + }, + { + brand: 'Google Chrome', + version: [ '106', '0', '5249', '119' ] + }, + { + brand: 'Not;A=Brand', + version: [ '99', '0', '0', '0' ] + } + ], + mobile: 0, + model: '', + }, }, userIDs: { kargoID: '5f108831-302d-11e7-bf6b-4595acd3bf6c', @@ -280,13 +328,39 @@ describe('kargo adapter tests', function () { prebidRawBidRequests: [ { bidId: 1, + ortb2: { + device: { + sua: { + platform: { + brand: 'macOS', + version: [ '12', '6', '0' ] + }, + browsers: [ + { + brand: 'Chromium', + version: [ '106', '0', '5249', '119' ] + }, + { + brand: 'Google Chrome', + version: [ '106', '0', '5249', '119' ] + }, + { + brand: 'Not;A=Brand', + version: [ '99', '0', '0', '0' ] + } + ], + mobile: 0, + model: '' + } + } + }, params: { placementId: 'foo' }, userId: { tdid: 'fake-tdid' }, - sizes: [[320, 50], [300, 250], [300, 600]] + sizes: [[320, 50], [300, 250], [300, 600]], }, { bidId: 2, @@ -330,7 +404,6 @@ describe('kargo adapter tests', function () { var payload = { timeout: 200, uspConsent: '1---', - foo: 'bar', refererInfo: { page: 'https://www.prebid.org', }, @@ -349,7 +422,6 @@ describe('kargo adapter tests', function () { expect(request.method).to.equal('GET'); expect(request.currency).to.equal('USD'); expect(request.timeout).to.equal(200); - expect(request.foo).to.equal('bar'); expect(krakenParams).to.deep.equal(expected); // Make sure session ID stays the same across requests simulating multiple auctions on one page load for (let i in sessionIds) { From 475dcd2487f8d87942a0dcfbda408a5e2b1a37d5 Mon Sep 17 00:00:00 2001 From: vfourny <109095331+vfourny-ogury@users.noreply.github.com> Date: Mon, 7 Nov 2022 14:51:17 +0100 Subject: [PATCH 414/569] adding timeline data to logging events (#9208) --- modules/oguryBidAdapter.js | 7 +++-- test/spec/modules/oguryBidAdapter_spec.js | 32 +++++++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index cb082f2d9a9..da62ce5c0a1 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -11,7 +11,7 @@ const DEFAULT_TIMEOUT = 1000; const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; const TIMEOUT_MONITORING_HOST = 'https://ms-ads-monitoring-events.presage.io'; const MS_COOKIE_SYNC_DOMAIN = 'https://ms-cookie-sync.presage.io'; -const ADAPTER_VERSION = '1.3.0'; +const ADAPTER_VERSION = '1.4.0'; function getClientWidth() { const documentElementClientWidth = window.top.document.documentElement.clientWidth @@ -114,7 +114,10 @@ function buildRequests(validBidRequests, bidderRequest) { banner: { format: sizes }, - ext: bidRequest.params + ext: { + ...bidRequest.params, + timeSpentOnPage: document.timeline && document.timeline.currentTime ? document.timeline.currentTime : 0 + } }); } }); diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index b9e4b44b571..f7cdcc0d11a 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -244,12 +244,16 @@ describe('OguryBidAdapter', function () { describe('buildRequests', function () { const stubbedWidth = 200 const stubbedHeight = 600 + const stubbedCurrentTime = 1234567890 const stubbedWidthMethod = sinon.stub(window.top.document.documentElement, 'clientWidth').get(function() { return stubbedWidth; }); const stubbedHeightMethod = sinon.stub(window.top.document.documentElement, 'clientHeight').get(function() { return stubbedHeight; }); + const stubbedCurrentTimeMethod = sinon.stub(document.timeline, 'currentTime').get(function() { + return stubbedCurrentTime; + }); const defaultTimeout = 1000; const expectedRequestObject = { @@ -266,7 +270,10 @@ describe('OguryBidAdapter', function () { h: 250 }] }, - ext: bidRequests[0].params + ext: { + ...bidRequests[0].params, + timeSpentOnPage: stubbedCurrentTime + } }, { id: bidRequests[1].bidId, tagid: bidRequests[1].params.adUnitId, @@ -276,7 +283,10 @@ describe('OguryBidAdapter', function () { h: 500 }] }, - ext: bidRequests[1].params + ext: { + ...bidRequests[1].params, + timeSpentOnPage: stubbedCurrentTime + } }], regs: { ext: { @@ -295,7 +305,7 @@ describe('OguryBidAdapter', function () { }, ext: { prebidversion: '$prebid.version$', - adapterversion: '1.3.0' + adapterversion: '1.4.0' }, device: { w: stubbedWidth, @@ -306,6 +316,7 @@ describe('OguryBidAdapter', function () { after(function() { stubbedWidthMethod.restore(); stubbedHeightMethod.restore(); + stubbedCurrentTimeMethod.restore(); }); it('sends bid request to ENDPOINT via POST', function () { @@ -316,6 +327,17 @@ describe('OguryBidAdapter', function () { expect(request.method).to.equal('POST'); }); + it('timeSpentOnpage should be 0 if timeline is undefined', function () { + const stubbedTimelineMethod = sinon.stub(document, 'timeline').get(function() { + return undefined; + }); + const validBidRequests = utils.deepClone(bidRequests) + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data.imp[0].ext.timeSpentOnPage).to.equal(0); + stubbedTimelineMethod.restore(); + }); + it('bid request object should be conform', function () { const validBidRequests = utils.deepClone(bidRequests) @@ -675,7 +697,7 @@ describe('OguryBidAdapter', function () { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain }, nurl: openRtbBidResponse.body.seatbid[0].bid[0].nurl, - adapterVersion: '1.3.0', + adapterVersion: '1.4.0', prebidVersion: '$prebid.version$' }, { requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, @@ -692,7 +714,7 @@ describe('OguryBidAdapter', function () { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain }, nurl: openRtbBidResponse.body.seatbid[0].bid[1].nurl, - adapterVersion: '1.3.0', + adapterVersion: '1.4.0', prebidVersion: '$prebid.version$' }] From 2ec27435d338bd4d4ac41d39d250ad5325137f85 Mon Sep 17 00:00:00 2001 From: Aaron Price <67345931+AaronColbyPrice@users.noreply.github.com> Date: Mon, 7 Nov 2022 06:36:21 -0800 Subject: [PATCH 415/569] eps_update_branding - Update Conversant branding to Epsilon (#9206) --- modules/conversantAnalyticsAdapter.md | 25 ++++++++++++------------- modules/conversantBidAdapter.md | 6 +++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/modules/conversantAnalyticsAdapter.md b/modules/conversantAnalyticsAdapter.md index 51db983a8bd..2f026cbcbb9 100644 --- a/modules/conversantAnalyticsAdapter.md +++ b/modules/conversantAnalyticsAdapter.md @@ -1,37 +1,36 @@ # Overview -- Module Name: Conversant Bidder Adapter +- Module Name: Epsilon Analytics Adapter - Module Type: Analytics Adapter -- Maintainer: mediapsr@conversantmedia.com +- Maintainer: mediapsr@epsilon.com ## Description -Analytics adapter for Conversant is used to track performance of Prebid auctions. See the usage below for how to +Analytics adapter for Epsilon (formerly Conversant) is used to track performance of Prebid auctions. See the usage below for how to configure the adapter for your webpage. To enable analytics and gain access to the data publishers will need - to contact their Conversant representative. + to contact their Epsilon representative (publishersupport@epsilon.com). ## Setup -Before any analytics are recorded for your website you will need to have a Conversant representative turn +Before any analytics are recorded for your website you will need to have an Epsilon representative turn on Prebid analytics for your website. -The simplest configuration to add Conversant Prebid analytics to your page is as follows: +The simplest configuration to add Epsilon Prebid analytics to your page is as follows: ``` pbjs.que.push(function() { pbjs.enableAnalytics({ provider: 'conversant', options: { - site_id: '' + site_id: } }) }); ``` -Additionally the following options are supported: +Additionally, the following options are supported: -- **sampleRate**: Expects an integer from 0 to 100. By default only 50% (corresponds to a value of 50) of instances are enabled -for Conversant Prebid Analytics. This field allows the publisher to override that percentage to sample -at a higher or lower rate. '0' would completely disable sampling and '100' would capture every instance. +- **cnvr_sampling**: Sample rate for analytics data. Value should be between 0 and 1 (inclusive), 0 == never sample, +1 == always sample, 0.5 == send analytics 50% of the time. ### Complete Example ``` @@ -39,8 +38,8 @@ at a higher or lower rate. '0' would completely disable sampling and '100' would pbjs.enableAnalytics({ provider: 'conversant', options: { - site_id: '1234', - sampleRate: 90 + site_id: 1234, + cnvr_sampling: 0.9 } }) }); diff --git a/modules/conversantBidAdapter.md b/modules/conversantBidAdapter.md index 07d9abf918b..baf8b756aca 100644 --- a/modules/conversantBidAdapter.md +++ b/modules/conversantBidAdapter.md @@ -1,12 +1,12 @@ # Overview -- Module Name: Conversant Bidder Adapter +- Module Name: Epsilon Bidder Adapter - Module Type: Bidder Adapter -- Maintainer: mediapsr@conversantmedia.com +- Maintainer: mediapsr@epsilon.com # Description -Module that connects to Conversant's demand sources. Supports banners and videos. +Module that connects to Epsilon's (formerly Conversant) demand sources. Supports banners and videos. # Test Parameters ``` From 40d3ae10fac6e71bf5811a1f9d657eb3184098a1 Mon Sep 17 00:00:00 2001 From: Mikhail Ivanchenko Date: Mon, 7 Nov 2022 23:54:10 +0300 Subject: [PATCH 416/569] NextMillennium Bid Adapter: set content-type to text/plain (#9210) * add video support * Changed content-type to text/plain --- modules/nextMillenniumBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 25ad1f43e82..762c571f1e6 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -141,7 +141,7 @@ export const spec = { url: isTest ? TEST_ENDPOINT : ENDPOINT, data: JSON.stringify(postBody), options: { - contentType: 'application/json', + contentType: 'text/plain', withCredentials: true }, From e28494b80caddfc348ae91fa53c3e8406cc425da Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 8 Nov 2022 06:21:26 -0800 Subject: [PATCH 417/569] default 1x1 size if no sizes on adUnit (#9211) --- modules/magniteAnalyticsAdapter.js | 2 +- .../modules/magniteAnalyticsAdapter_spec.js | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/magniteAnalyticsAdapter.js b/modules/magniteAnalyticsAdapter.js index 44666178e46..99f384e9eff 100644 --- a/modules/magniteAnalyticsAdapter.js +++ b/modules/magniteAnalyticsAdapter.js @@ -706,7 +706,7 @@ magniteAdapter.track = ({ eventType, args }) => { 'code as adUnitCode', 'transactionId', 'mediaTypes', mediaTypes => Object.keys(mediaTypes), - 'sizes as dimensions', sizes => sizes.map(sizeToDimensions), + 'sizes as dimensions', sizes => (sizes || [[1, 1]]).map(sizeToDimensions), ]); ad.pbAdSlot = deepAccess(adUnit, 'ortb2Imp.ext.data.pbadslot'); ad.pattern = deepAccess(adUnit, 'ortb2Imp.ext.data.aupname'); diff --git a/test/spec/modules/magniteAnalyticsAdapter_spec.js b/test/spec/modules/magniteAnalyticsAdapter_spec.js index 240a5aa51a2..69fd7794ced 100644 --- a/test/spec/modules/magniteAnalyticsAdapter_spec.js +++ b/test/spec/modules/magniteAnalyticsAdapter_spec.js @@ -548,6 +548,26 @@ describe('magnite analytics adapter', function () { ]); }); + it('should pass along 1x1 size if no sizes in adUnit', function () { + const auctionInit = utils.deepClone(MOCK.AUCTION_INIT); + + delete auctionInit.adUnits[0].sizes; + + events.emit(AUCTION_INIT, auctionInit); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + clock.tick(rubiConf.analyticsBatchTimeout + 1000); + + let message = JSON.parse(server.requests[0].requestBody); + expect(message.auctions[0].adUnits[0].dimensions).to.deep.equal([ + { + width: 1, + height: 1 + } + ]); + }); + it('should pass along user ids', function () { let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); auctionInit.bidderRequests[0].bids[0].userId = { From e19b7f146162da624caebd03704237281235d702 Mon Sep 17 00:00:00 2001 From: Nayan Savla Date: Tue, 8 Nov 2022 06:49:58 -0800 Subject: [PATCH 418/569] Yieldmo Bid Adapteer : add iab gvlid and update path (#9189) * Add stage bidder service * add stage url * fix url * fix string method * clean up rebase * Adding transaction id and auction id. * Adding gvlid to Yieldmo Adapter * Resolving linting errors * Update yieldmoBidAdapter_spec.js Co-authored-by: elber@yieldmo.com Co-authored-by: Elber Carneiro <81249884+ym-elber@users.noreply.github.com> --- modules/yieldmoBidAdapter.js | 30 +++++++++++++++------ test/spec/modules/yieldmoBidAdapter_spec.js | 7 +++-- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 892936ceea5..64ab8a87eea 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -20,12 +20,15 @@ import {find, includes} from '../src/polyfill.js'; import {createEidsArray} from './userId/eids.js'; const BIDDER_CODE = 'yieldmo'; +const GVLID = 173; const CURRENCY = 'USD'; const TIME_TO_LIVE = 300; const NET_REVENUE = true; -const BANNER_SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebid'; -const VIDEO_SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebidvideo'; const PB_COOKIE_ASSIST_SYNC_ENDPOINT = `https://ads.yieldmo.com/pbcas`; +const BANNER_PATH = '/exchange/prebid'; +const VIDEO_PATH = '/exchange/prebidvideo'; +const STAGE_DOMAIN = 'https://ads-stg.yieldmo.com'; +const PROD_DOMAIN = 'https://ads.yieldmo.com'; const OUTSTREAM_VIDEO_PLAYER_URL = 'https://prebid-outstream.yieldmo.com/bundle.js'; const OPENRTB_VIDEO_BIDPARAMS = ['mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', 'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable']; @@ -40,7 +43,7 @@ const BANNER_REQUEST_PROPERTIES_TO_REDUCE = ['description', 'title', 'pr', 'page export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], - + gvlid: GVLID, /** * Determines whether or not the given bid request is valid. * @param {object} bid, bid to validate @@ -59,6 +62,9 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (bidRequests, bidderRequest) { + const stage = isStage(bidderRequest); + const bannerUrl = getAdserverUrl(BANNER_PATH, stage); + const videoUrl = getAdserverUrl(VIDEO_PATH, stage); const bannerBidRequests = bidRequests.filter(request => hasBannerMediaType(request)); const videoBidRequests = bidRequests.filter(request => hasVideoMediaType(request)); let serverRequests = []; @@ -122,8 +128,8 @@ export const spec = { serverRequest.eids = JSON.stringify(eids); }; // check if url exceeded max length - const url = `${BANNER_SERVER_ENDPOINT}?${parseQueryStringParameters(serverRequest)}`; - let extraCharacters = url.length - MAX_BANNER_REQUEST_URL_LENGTH; + const fullUrl = `${bannerUrl}?${parseQueryStringParameters(serverRequest)}`; + let extraCharacters = fullUrl.length - MAX_BANNER_REQUEST_URL_LENGTH; if (extraCharacters > 0) { for (let i = 0; i < BANNER_REQUEST_PROPERTIES_TO_REDUCE.length; i++) { extraCharacters = shortcutProperty(extraCharacters, serverRequest, BANNER_REQUEST_PROPERTIES_TO_REDUCE[i]); @@ -136,7 +142,7 @@ export const spec = { serverRequests.push({ method: 'GET', - url: BANNER_SERVER_ENDPOINT, + url: bannerUrl, data: serverRequest }); } @@ -148,7 +154,7 @@ export const spec = { }; serverRequests.push({ method: 'POST', - url: VIDEO_SERVER_ENDPOINT, + url: videoUrl, data: serverRequest }); } @@ -249,7 +255,6 @@ function addPlacement(request) { if (transactionId) { placementInfo.tid = transactionId; } - if (request.auctionId) { placementInfo.auctionId = request.auctionId; } @@ -676,3 +681,12 @@ function canAccessTopWindow() { return false; } } + +function isStage(bidderRequest) { + return !!bidderRequest.refererInfo?.referer?.includes('pb_force_a'); +} + +function getAdserverUrl(path, stage) { + const domain = stage ? STAGE_DOMAIN : PROD_DOMAIN; + return `${domain}${path}`; +} diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 32f0ead11f1..8a0ebf863dd 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -34,6 +34,7 @@ describe('YieldmoAdapter', function () { userId: { tdid: '8d146286-91d4-4958-aff4-7e489dd1abd6' }, + transactionId: '54a58774-7a41-494e-9aaf-fa7b79164f0c', ...rootParams }); @@ -63,6 +64,7 @@ describe('YieldmoAdapter', function () { ...videoParams } }, + transactionId: '54a58774-7a41-494e-8cbc-fa7b79164f0c', ...rootParams }); @@ -178,10 +180,9 @@ describe('YieldmoAdapter', function () { expect(buildAndGetPlacementInfo(bidArray)).to.equal( '[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]],"bidFloor":0.1,"auctionId":"1d1a030790a475"}]' ); - // multiple placements bidArray.push(mockBannerBid( - {adUnitCode: 'adunit-2', bidId: '123a', bidderRequestId: '321', auctionId: '222'}, {bidFloor: 0.2})); + {adUnitCode: 'adunit-2', bidId: '123a', bidderRequestId: '321', auctionId: '222', transactionId: '444'}, {bidFloor: 0.2})); expect(buildAndGetPlacementInfo(bidArray)).to.equal( '[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]],"bidFloor":0.1,"auctionId":"1d1a030790a475"},' + '{"placement_id":"adunit-2","callback_id":"123a","sizes":[[300,250],[300,600]],"bidFloor":0.2,"auctionId":"222"}]' @@ -193,7 +194,6 @@ describe('YieldmoAdapter', function () { let placementInfo = buildAndGetPlacementInfo(bidArray); expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"'); expect(placementInfo).not.to.include('"ym_placement_id":"ym_0987654321"'); - bidArray.push(mockBannerBid({}, {placementId: 'ym_0987654321'})); placementInfo = buildAndGetPlacementInfo(bidArray); expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"'); @@ -507,7 +507,6 @@ describe('YieldmoAdapter', function () { it('should add auction id to video bid request', function() { const auctionId = '1d1a03073455'; - expect(buildAndGetData([mockVideoBid({})]).auctionId).to.deep.equal(auctionId); }); From 8e023b161a54726db4001b7fdd98fd71808952b1 Mon Sep 17 00:00:00 2001 From: dalmenarDevST <116064809+dalmenarDevST@users.noreply.github.com> Date: Tue, 8 Nov 2022 16:08:04 +0100 Subject: [PATCH 419/569] add coppa param to payload (#9202) --- modules/seedtagBidAdapter.js | 13 ++++-- test/spec/modules/seedtagBidAdapter_spec.js | 44 ++++++++++++++++----- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index 5c4a3af866c..850ac5610f5 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -1,6 +1,7 @@ import { isArray, _map, triggerPixel } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js' import { VIDEO, BANNER } from '../src/mediaTypes.js' +import { config } from '../src/config.js'; const BIDDER_CODE = 'seedtag'; const SEEDTAG_ALIAS = 'st'; @@ -177,7 +178,7 @@ function ttfb() { return ttfb >= 0 && ttfb <= performance.now() ? ttfb : 0; } -export function getTimeoutUrl (data) { +export function getTimeoutUrl(data) { let queryParams = ''; if ( isArray(data) && data[0] && @@ -235,7 +236,6 @@ export const spec = { if (gdprApplies !== undefined) payload['ga'] = gdprApplies; payload['cd'] = bidderRequest.gdprConsent.consentString; } - if (bidderRequest.uspConsent) { payload['uspConsent'] = bidderRequest.uspConsent } @@ -244,6 +244,11 @@ export const spec = { payload.schain = validBidRequests[0].schain; } + let coppa = config.getConfig('coppa') + if (coppa) { + payload.coppa = coppa + } + const payloadString = JSON.stringify(payload) return { method: 'POST', @@ -258,10 +263,10 @@ export const spec = { * @param {ServerResponse} serverResponse A successful response from the server. * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse) { + interpretResponse: function (serverResponse) { const serverBody = serverResponse.body; if (serverBody && serverBody.bids && isArray(serverBody.bids)) { - return _map(serverBody.bids, function(bid) { + return _map(serverBody.bids, function (bid) { return buildBidResponse(bid); }); } else { diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index 05d609e8003..bd726bfe971 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec, getTimeoutUrl } from 'modules/seedtagBidAdapter.js'; import * as utils from 'src/utils.js'; +import { config } from '../../../src/config.js'; const PUBLISHER_ID = '0000-0000-01'; const ADUNIT_ID = '000000'; @@ -357,6 +358,29 @@ describe('Seedtag Adapter', function () { }); }); + describe('COPPA param', function () { + it('should add COPPA param to payload when prebid config has parameter COPPA equal to true', function () { + config.setConfig({ coppa: true }) + + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + expect(data.coppa).to.equal(true); + }) + + it('should not add COPPA param to payload when prebid config has parameter COPPA equal to false', function () { + config.setConfig({ coppa: false }) + + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + expect(data.coppa).to.be.undefined; + }) + + it('should not add COPPA param to payload when prebid config has not parameter COPPA', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + expect(data.coppa).to.be.undefined; + }) + }) describe('schain param', function () { it('should add schain to payload when exposed on validBidRequest', function () { // https://github.com/prebid/Prebid.js/blob/master/modules/schain.md#sample-code-for-passing-the-schain-object @@ -536,11 +560,11 @@ describe('Seedtag Adapter', function () { const timeoutUrl = getTimeoutUrl(timeoutData); expect(timeoutUrl).to.equal( 'https://s.seedtag.com/se/hb/timeout?publisherToken=' + - params.publisherId + - '&adUnitId=' + - params.adUnitId + - '&timeout=' + - timeout + params.publisherId + + '&adUnitId=' + + params.adUnitId + + '&timeout=' + + timeout ); }); @@ -552,11 +576,11 @@ describe('Seedtag Adapter', function () { expect( utils.triggerPixel.calledWith( 'https://s.seedtag.com/se/hb/timeout?publisherToken=' + - params.publisherId + - '&adUnitId=' + - params.adUnitId + - '&timeout=' + - timeout + params.publisherId + + '&adUnitId=' + + params.adUnitId + + '&timeout=' + + timeout ) ).to.equal(true); }); From 7046fc2effc22e6b21e7e78954b3f2d0f785554f Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 8 Nov 2022 07:21:01 -0800 Subject: [PATCH 420/569] Core & userId module: reload userIDs when GDPR consent changes (#9190) --- modules/userId/index.js | 36 +++++++++++++--------- src/consentHandler.js | 24 ++++++++++++++- test/helpers/consentData.js | 11 +++++-- test/spec/modules/userId_spec.js | 33 ++++++++++++++++++-- test/spec/unit/core/consentHandler_spec.js | 34 ++++++++++++++++++++ 5 files changed, 118 insertions(+), 20 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 8fdd4319dfc..9e9eb5057bc 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -588,20 +588,28 @@ function idSystemInitializer({delay = GreedyPromise.timeout} = {}) { return gdprDataHandler.promise.finally(initMetrics.startTiming('userId.init.gdpr')); } - let done = cancelAndTry( - GreedyPromise.all([hooksReady, startInit.promise]) - .then(timeGdpr) - .then(checkRefs((consentData) => { - initSubmodules(initModules, allModules, consentData); - })) - .then(() => startCallbacks.promise.finally(initMetrics.startTiming('userId.callbacks.pending'))) - .then(checkRefs(() => { - const modWithCb = initModules.filter(item => isFn(item.callback)); - if (modWithCb.length) { - return new GreedyPromise((resolve) => processSubmoduleCallbacks(modWithCb, resolve)); - } - })) - ); + let done = GreedyPromise.resolve(); + + function loadIds() { + done = cancelAndTry( + done.catch(() => null) + .then(() => GreedyPromise.all([hooksReady, startInit.promise])) + .then(timeGdpr) + .then(checkRefs((consentData) => { + initSubmodules(initModules, allModules, consentData); + })) + .then(() => startCallbacks.promise.finally(initMetrics.startTiming('userId.callbacks.pending'))) + .then(checkRefs(() => { + const modWithCb = initModules.filter(item => isFn(item.callback)); + if (modWithCb.length) { + return new GreedyPromise((resolve) => processSubmoduleCallbacks(modWithCb, resolve)); + } + })) + ); + } + + loadIds(); + gdprDataHandler.onConsentChange(loadIds); /** * with `ready` = true, starts initialization; with `refresh` = true, reinitialize submodules (optionally diff --git a/src/consentHandler.js b/src/consentHandler.js index 861a9894a2c..4a56cb4c402 100644 --- a/src/consentHandler.js +++ b/src/consentHandler.js @@ -1,11 +1,13 @@ -import {isStr, timestamp} from './utils.js'; +import {isStr, timestamp, deepEqual, logError} from './utils.js'; import {defer, GreedyPromise} from './utils/promise.js'; export class ConsentHandler { #enabled; #data; + #hasData = false; #defer; #ready; + #listeners; generatedTime; constructor() { @@ -14,8 +16,19 @@ export class ConsentHandler { #resolve(data) { this.#ready = true; + const hasChanged = !this.#hasData || !deepEqual(this.#data, data); this.#data = data; + this.#hasData = true; this.#defer.resolve(data); + if (hasChanged) { + this.#listeners.forEach(cb => { + try { + cb(data) + } catch (e) { + logError(e); + } + }) + } } /** @@ -27,6 +40,15 @@ export class ConsentHandler { this.#data = null; this.#ready = false; this.generatedTime = null; + this.#listeners = []; + } + + /** + * Register a callback to run each time consent data changes. + * @param {(consentData) => any} fn + */ + onConsentChange(fn) { + this.#listeners.push(fn); } /** diff --git a/test/helpers/consentData.js b/test/helpers/consentData.js index c708e397bd6..841e19ffe52 100644 --- a/test/helpers/consentData.js +++ b/test/helpers/consentData.js @@ -2,9 +2,14 @@ import {gdprDataHandler} from 'src/adapterManager.js'; import {GreedyPromise} from '../../src/utils/promise.js'; export function mockGdprConsent(sandbox, getConsentData = () => null) { - sandbox.stub(gdprDataHandler, 'enabled').get(() => true) - sandbox.stub(gdprDataHandler, 'promise').get(() => GreedyPromise.resolve(getConsentData())); - sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(getConsentData) + const s1 = sandbox.stub(gdprDataHandler, 'enabled').get(() => true) + const s2 = sandbox.stub(gdprDataHandler, 'promise').get(() => GreedyPromise.resolve(getConsentData())); + const s3 = sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(getConsentData) + return function unMock() { + s1.restore(); + s2.restore(); + s3.restore(); + } } beforeEach(() => { diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index f4abcc4b9f6..e4c6891b967 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -53,6 +53,8 @@ import {hook} from '../../../src/hook.js'; import {mockGdprConsent} from '../../helpers/consentData.js'; import {getPPID} from '../../../src/adserver.js'; import {uninstall as uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; +import {gdprDataHandler} from '../../../src/adapterManager.js'; +import {GreedyPromise} from '../../../src/utils/promise.js'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -149,6 +151,8 @@ describe('User ID', function () { localStorage.removeItem(PBJS_USER_ID_OPTOUT_NAME); }); + let restoreGdprConsent; + beforeEach(function () { // TODO: this whole suite needs to be redesigned; it is passing by accident // some tests do not pass if consent data is available @@ -157,7 +161,7 @@ describe('User ID', function () { resetConsentData(); sandbox = sinon.sandbox.create(); consentData = null; - mockGdprConsent(sandbox, () => consentData); + restoreGdprConsent = mockGdprConsent(sandbox, () => consentData); coreStorage.setCookie(CONSENT_LOCAL_STORAGE_NAME, '', EXPIRED_COOKIE_DATE); }); @@ -985,6 +989,7 @@ describe('User ID', function () { let adUnits; let mockIdCallback; let auctionSpy; + let mockIdSystem; beforeEach(function () { sandbox = sinon.createSandbox(); @@ -998,7 +1003,7 @@ describe('User ID', function () { auctionSpy = sandbox.spy(); mockIdCallback = sandbox.stub(); - const mockIdSystem = { + mockIdSystem = { name: 'mockId', decode: function (value) { return { @@ -1023,6 +1028,30 @@ describe('User ID', function () { sandbox.restore(); }); + it('waits for GDPR if it was enabled after userId', () => { + restoreGdprConsent(); + mockIdSystem.getId = function (_, consent) { + if (consent?.given) { + return {id: {MOCKID: 'valid'}}; + } else { + return {id: {MOCKID: 'invalid'}}; + } + } + config.setConfig({ + userSync: { + auctionDelay: 0, + userIds: [{ + name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + }] + } + }); + const consent = {given: true}; + gdprDataHandler.setConsentData(consent); + return expectImmediateBidHook(auctionSpy, {adUnits}).then(() => { + expect(adUnits[0].bids[0].userId.mid).to.eql('valid'); + }) + }) + it('delays auction if auctionDelay is set, timing out at auction delay', function () { config.setConfig({ userSync: { diff --git a/test/spec/unit/core/consentHandler_spec.js b/test/spec/unit/core/consentHandler_spec.js index 082ff34f90c..9d5170eb667 100644 --- a/test/spec/unit/core/consentHandler_spec.js +++ b/test/spec/unit/core/consentHandler_spec.js @@ -56,4 +56,38 @@ describe('Consent data handler', () => { }) }) }); + + it('should run onConsentChange listeners when consent data changes', () => { + handler.setConsentData({consent: 'data'}); + const listener = sinon.stub(); + handler.onConsentChange(listener); + handler.setConsentData({consent: 'data'}); + sinon.assert.notCalled(listener); + const newConsent = {other: 'data'}; + handler.setConsentData(newConsent); + sinon.assert.calledWith(listener, newConsent); + }); + + it('should not choke if listener throws', () => { + handler.onConsentChange(sinon.stub().throws(new Error())); + const listener = sinon.stub(); + handler.onConsentChange(listener); + const consent = {consent: 'data'}; + handler.setConsentData(consent); + sinon.assert.calledWith(listener, consent); + }); + + Object.entries({ + 'undefined': undefined, + 'null': null + }).forEach(([t, val]) => { + it(`should run onConsentChange when consent is first set to ${t}`, () => { + const listener = sinon.stub(); + handler.onConsentChange(listener); + handler.setConsentData(val); + handler.setConsentData(val); + sinon.assert.calledOnce(listener); + sinon.assert.calledWith(listener, val); + }) + }) }) From 152ca5202b1d3a3f3c4365038f3b7f7445dd8a44 Mon Sep 17 00:00:00 2001 From: mjaworskiccx <50406214+mjaworskiccx@users.noreply.github.com> Date: Tue, 8 Nov 2022 16:57:28 +0100 Subject: [PATCH 421/569] Clickonometrics Bid Adapter : add gvlid (#9198) * adomain support * adomain support * adomain support * adomain support * adomain support * video params * docs changes * Clickonometrics adapter update --- modules/ccxBidAdapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 7c6b0411023..331bace7928 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -8,6 +8,7 @@ const BID_URL = 'https://delivery.clickonometrics.pl/ortb/prebid/bid' const SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6] const SUPPORTED_VIDEO_MIMES = ['video/mp4', 'video/x-flv'] const SUPPORTED_VIDEO_PLAYBACK_METHODS = [1, 2, 3, 4] +const GVLID = 773; function _getDeviceObj () { let device = {} @@ -19,8 +20,7 @@ function _getDeviceObj () { function _getSiteObj (bidderRequest) { let site = {} - // TODO: does the fallback to window.location make sense? - let url = bidderRequest?.refererInfo?.page || window.location.href + let url = bidderRequest?.refererInfo?.page if (url.length > 0) { url = url.split('?')[0] } @@ -140,6 +140,7 @@ function _buildResponse (bid, currency, ttl) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { From 6d114e83725b403fadd889202b449de225db7275 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 8 Nov 2022 09:24:06 -0700 Subject: [PATCH 422/569] Revert "Clickonometrics Bid Adapter : add gvlid (#9198)" (#9216) This reverts commit 152ca5202b1d3a3f3c4365038f3b7f7445dd8a44. --- modules/ccxBidAdapter.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 331bace7928..7c6b0411023 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -8,7 +8,6 @@ const BID_URL = 'https://delivery.clickonometrics.pl/ortb/prebid/bid' const SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6] const SUPPORTED_VIDEO_MIMES = ['video/mp4', 'video/x-flv'] const SUPPORTED_VIDEO_PLAYBACK_METHODS = [1, 2, 3, 4] -const GVLID = 773; function _getDeviceObj () { let device = {} @@ -20,7 +19,8 @@ function _getDeviceObj () { function _getSiteObj (bidderRequest) { let site = {} - let url = bidderRequest?.refererInfo?.page + // TODO: does the fallback to window.location make sense? + let url = bidderRequest?.refererInfo?.page || window.location.href if (url.length > 0) { url = url.split('?')[0] } @@ -140,7 +140,6 @@ function _buildResponse (bid, currency, ttl) { export const spec = { code: BIDDER_CODE, - gvlid: GVLID, supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { From fe23164af37b647d561f03831027d9352495772f Mon Sep 17 00:00:00 2001 From: Brett Bloxom <38990705+BrettBlox@users.noreply.github.com> Date: Tue, 8 Nov 2022 12:00:59 -0700 Subject: [PATCH 423/569] Concert Bid Adapter: Update localStorage name-spacing for Concert UID (#9158) * collect EIDs for bid request * add ad slot positioning to payload * RPO-2012: Update local storage name-spacing for c_uid (#8) * Updates c_uid namespacing to be more specific for concert * fixes unit tests * remove console.log * RPO-2012: Add check for shared id (#9) * Adds check for sharedId * Updates cookie name * remove trailing comma Co-authored-by: antoin Co-authored-by: Antoin --- modules/concertBidAdapter.js | 20 ++++++++-- test/spec/modules/concertBidAdapter_spec.js | 41 +++++++++++++-------- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js index 620e77a5a08..dcea60d5231 100644 --- a/modules/concertBidAdapter.js +++ b/modules/concertBidAdapter.js @@ -1,4 +1,4 @@ -import { logWarn, logMessage, debugTurnedOn, generateUUID } from '../src/utils.js'; +import { logWarn, logMessage, debugTurnedOn, generateUUID, deepAccess } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { hasPurpose1Consent } from '../src/utils/gpdr.js'; @@ -178,7 +178,7 @@ export const spec = { registerBidder(spec); -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({bidderCode: BIDDER_CODE}); /** * Check or generate a UID for the current user. @@ -188,10 +188,24 @@ function getUid(bidderRequest) { return false; } - const CONCERT_UID_KEY = 'c_uid'; + const sharedId = deepAccess(bidderRequest, 'userId._sharedid.id'); + if (sharedId) { + return sharedId; + } + + const LEGACY_CONCERT_UID_KEY = 'c_uid'; + const CONCERT_UID_KEY = 'vmconcert_uid'; + + const legacyUid = storage.getDataFromLocalStorage(LEGACY_CONCERT_UID_KEY); let uid = storage.getDataFromLocalStorage(CONCERT_UID_KEY); + if (legacyUid) { + uid = legacyUid; + storage.setDataInLocalStorage(CONCERT_UID_KEY, uid); + storage.removeDataFromLocalStorage(LEGACY_CONCERT_UID_KEY); + } + if (!uid) { uid = generateUUID(); storage.setDataInLocalStorage(CONCERT_UID_KEY, uid); diff --git a/test/spec/modules/concertBidAdapter_spec.js b/test/spec/modules/concertBidAdapter_spec.js index 10ea997b304..00626472ecb 100644 --- a/test/spec/modules/concertBidAdapter_spec.js +++ b/test/spec/modules/concertBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import sinon from 'sinon'; -import { spec } from 'modules/concertBidAdapter.js'; -import { getStorageManager } from '../../../src/storageManager.js' +import { spec, storage } from 'modules/concertBidAdapter.js'; +import { hook } from 'src/hook.js'; describe('ConcertAdapter', function () { let bidRequests; @@ -10,9 +10,8 @@ describe('ConcertAdapter', function () { let element; let sandbox; - afterEach(function () { - $$PREBID_GLOBAL$$.bidderSettings = {}; - sandbox.restore(); + before(function () { + hook.ready(); }); beforeEach(function () { @@ -39,6 +38,7 @@ describe('ConcertAdapter', function () { storageAllowed: true } }; + bidRequests = [ { bidder: 'concert', @@ -83,6 +83,11 @@ describe('ConcertAdapter', function () { sandbox.stub(document, 'getElementById').withArgs('desktop_leaderboard_variable').returns(element) }); + afterEach(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + sandbox.restore(); + }); + describe('spec.isBidRequestValid', function() { it('should return when it recieved all the required params', function() { const bid = bidRequests[0]; @@ -118,7 +123,6 @@ describe('ConcertAdapter', function () { }); it('should not generate uid if the user has opted out', function() { - const storage = getStorageManager(); storage.setDataInLocalStorage('c_nap', 'true'); const request = spec.buildRequests(bidRequests, bidRequest); const payload = JSON.parse(request.data); @@ -127,7 +131,6 @@ describe('ConcertAdapter', function () { }); it('should generate uid if the user has not opted out', function() { - const storage = getStorageManager(); storage.removeDataFromLocalStorage('c_nap'); const request = spec.buildRequests(bidRequests, bidRequest); const payload = JSON.parse(request.data); @@ -135,9 +138,22 @@ describe('ConcertAdapter', function () { expect(payload.meta.uid).to.not.equal(false); }); - it('should grab uid from local storage if it exists', function() { - const storage = getStorageManager(); - storage.setDataInLocalStorage('c_uid', 'foo'); + it('should use sharedid if it exists', function() { + storage.removeDataFromLocalStorage('c_nap'); + const request = spec.buildRequests(bidRequests, { + ...bidRequest, + userId: { + _sharedid: { + id: '123abc' + } + } + }); + const payload = JSON.parse(request.data); + expect(payload.meta.uid).to.equal('123abc'); + }) + + it('should grab uid from local storage if it exists and sharedid does not', function() { + storage.setDataInLocalStorage('vmconcert_uid', 'foo'); storage.removeDataFromLocalStorage('c_nap'); const request = spec.buildRequests(bidRequests, bidRequest); const payload = JSON.parse(request.data); @@ -211,7 +227,6 @@ describe('ConcertAdapter', function () { const opts = { iframeEnabled: true }; - const storage = getStorageManager(); storage.setDataInLocalStorage('c_nap', 'true'); const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); @@ -222,7 +237,6 @@ describe('ConcertAdapter', function () { const opts = { iframeEnabled: true }; - const storage = getStorageManager(); storage.removeDataFromLocalStorage('c_nap'); bidRequest.gdprConsent = { @@ -237,7 +251,6 @@ describe('ConcertAdapter', function () { const opts = { iframeEnabled: true }; - const storage = getStorageManager(); storage.removeDataFromLocalStorage('c_nap'); bidRequest.gdprConsent = { @@ -252,7 +265,6 @@ describe('ConcertAdapter', function () { const opts = { iframeEnabled: true }; - const storage = getStorageManager(); storage.removeDataFromLocalStorage('c_nap'); bidRequest.gdprConsent = { @@ -268,7 +280,6 @@ describe('ConcertAdapter', function () { const opts = { iframeEnabled: true }; - const storage = getStorageManager(); storage.removeDataFromLocalStorage('c_nap'); bidRequest.gdprConsent = { From c018b183948696e8873989b9e239134b157a8f3d Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 9 Nov 2022 15:44:24 +0100 Subject: [PATCH 424/569] adagioBidAdapter: fix Video Ortb placement param validation (#9218) --- modules/adagioBidAdapter.js | 2 +- test/spec/modules/adagioBidAdapter_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index e59fd341bae..8dcd95f933b 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -59,7 +59,7 @@ export const ORTB_VIDEO_PARAMS = { 'w': (value) => isInteger(value), 'h': (value) => isInteger(value), 'startdelay': (value) => isInteger(value), - 'placement': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5].indexOf(v) !== -1), + 'placement': (value) => [1, 2, 3, 4, 5].indexOf(value) !== -1, 'linearity': (value) => [1, 2].indexOf(value) !== -1, 'skip': (value) => [0, 1].indexOf(value) !== -1, 'skipmin': (value) => isInteger(value), diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index e2fde8ff2e3..436d481c4a1 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -400,7 +400,7 @@ describe('Adagio bid adapter', () => { skipafter: 4, minduration: 10, maxduration: 30, - placement: [3], + placement: 3, protocols: [8] } }).build(); @@ -415,7 +415,7 @@ describe('Adagio bid adapter', () => { skipafter: 4, minduration: 10, maxduration: 30, - placement: [3], + placement: 3, protocols: [8], w: 300, h: 250 From 5e7ace9e3f499124181f1adddcd19a3488063f2a Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 9 Nov 2022 10:25:18 -0800 Subject: [PATCH 425/569] Revert "Core & userId module: reload userIDs when GDPR consent changes (#9190)" (#9225) This reverts commit 7046fc2effc22e6b21e7e78954b3f2d0f785554f. --- modules/userId/index.js | 36 +++++++++------------- src/consentHandler.js | 24 +-------------- test/helpers/consentData.js | 11 ++----- test/spec/modules/userId_spec.js | 33 ++------------------ test/spec/unit/core/consentHandler_spec.js | 34 -------------------- 5 files changed, 20 insertions(+), 118 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 9e9eb5057bc..8fdd4319dfc 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -588,28 +588,20 @@ function idSystemInitializer({delay = GreedyPromise.timeout} = {}) { return gdprDataHandler.promise.finally(initMetrics.startTiming('userId.init.gdpr')); } - let done = GreedyPromise.resolve(); - - function loadIds() { - done = cancelAndTry( - done.catch(() => null) - .then(() => GreedyPromise.all([hooksReady, startInit.promise])) - .then(timeGdpr) - .then(checkRefs((consentData) => { - initSubmodules(initModules, allModules, consentData); - })) - .then(() => startCallbacks.promise.finally(initMetrics.startTiming('userId.callbacks.pending'))) - .then(checkRefs(() => { - const modWithCb = initModules.filter(item => isFn(item.callback)); - if (modWithCb.length) { - return new GreedyPromise((resolve) => processSubmoduleCallbacks(modWithCb, resolve)); - } - })) - ); - } - - loadIds(); - gdprDataHandler.onConsentChange(loadIds); + let done = cancelAndTry( + GreedyPromise.all([hooksReady, startInit.promise]) + .then(timeGdpr) + .then(checkRefs((consentData) => { + initSubmodules(initModules, allModules, consentData); + })) + .then(() => startCallbacks.promise.finally(initMetrics.startTiming('userId.callbacks.pending'))) + .then(checkRefs(() => { + const modWithCb = initModules.filter(item => isFn(item.callback)); + if (modWithCb.length) { + return new GreedyPromise((resolve) => processSubmoduleCallbacks(modWithCb, resolve)); + } + })) + ); /** * with `ready` = true, starts initialization; with `refresh` = true, reinitialize submodules (optionally diff --git a/src/consentHandler.js b/src/consentHandler.js index 4a56cb4c402..861a9894a2c 100644 --- a/src/consentHandler.js +++ b/src/consentHandler.js @@ -1,13 +1,11 @@ -import {isStr, timestamp, deepEqual, logError} from './utils.js'; +import {isStr, timestamp} from './utils.js'; import {defer, GreedyPromise} from './utils/promise.js'; export class ConsentHandler { #enabled; #data; - #hasData = false; #defer; #ready; - #listeners; generatedTime; constructor() { @@ -16,19 +14,8 @@ export class ConsentHandler { #resolve(data) { this.#ready = true; - const hasChanged = !this.#hasData || !deepEqual(this.#data, data); this.#data = data; - this.#hasData = true; this.#defer.resolve(data); - if (hasChanged) { - this.#listeners.forEach(cb => { - try { - cb(data) - } catch (e) { - logError(e); - } - }) - } } /** @@ -40,15 +27,6 @@ export class ConsentHandler { this.#data = null; this.#ready = false; this.generatedTime = null; - this.#listeners = []; - } - - /** - * Register a callback to run each time consent data changes. - * @param {(consentData) => any} fn - */ - onConsentChange(fn) { - this.#listeners.push(fn); } /** diff --git a/test/helpers/consentData.js b/test/helpers/consentData.js index 841e19ffe52..c708e397bd6 100644 --- a/test/helpers/consentData.js +++ b/test/helpers/consentData.js @@ -2,14 +2,9 @@ import {gdprDataHandler} from 'src/adapterManager.js'; import {GreedyPromise} from '../../src/utils/promise.js'; export function mockGdprConsent(sandbox, getConsentData = () => null) { - const s1 = sandbox.stub(gdprDataHandler, 'enabled').get(() => true) - const s2 = sandbox.stub(gdprDataHandler, 'promise').get(() => GreedyPromise.resolve(getConsentData())); - const s3 = sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(getConsentData) - return function unMock() { - s1.restore(); - s2.restore(); - s3.restore(); - } + sandbox.stub(gdprDataHandler, 'enabled').get(() => true) + sandbox.stub(gdprDataHandler, 'promise').get(() => GreedyPromise.resolve(getConsentData())); + sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(getConsentData) } beforeEach(() => { diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index e4c6891b967..f4abcc4b9f6 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -53,8 +53,6 @@ import {hook} from '../../../src/hook.js'; import {mockGdprConsent} from '../../helpers/consentData.js'; import {getPPID} from '../../../src/adserver.js'; import {uninstall as uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; -import {gdprDataHandler} from '../../../src/adapterManager.js'; -import {GreedyPromise} from '../../../src/utils/promise.js'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -151,8 +149,6 @@ describe('User ID', function () { localStorage.removeItem(PBJS_USER_ID_OPTOUT_NAME); }); - let restoreGdprConsent; - beforeEach(function () { // TODO: this whole suite needs to be redesigned; it is passing by accident // some tests do not pass if consent data is available @@ -161,7 +157,7 @@ describe('User ID', function () { resetConsentData(); sandbox = sinon.sandbox.create(); consentData = null; - restoreGdprConsent = mockGdprConsent(sandbox, () => consentData); + mockGdprConsent(sandbox, () => consentData); coreStorage.setCookie(CONSENT_LOCAL_STORAGE_NAME, '', EXPIRED_COOKIE_DATE); }); @@ -989,7 +985,6 @@ describe('User ID', function () { let adUnits; let mockIdCallback; let auctionSpy; - let mockIdSystem; beforeEach(function () { sandbox = sinon.createSandbox(); @@ -1003,7 +998,7 @@ describe('User ID', function () { auctionSpy = sandbox.spy(); mockIdCallback = sandbox.stub(); - mockIdSystem = { + const mockIdSystem = { name: 'mockId', decode: function (value) { return { @@ -1028,30 +1023,6 @@ describe('User ID', function () { sandbox.restore(); }); - it('waits for GDPR if it was enabled after userId', () => { - restoreGdprConsent(); - mockIdSystem.getId = function (_, consent) { - if (consent?.given) { - return {id: {MOCKID: 'valid'}}; - } else { - return {id: {MOCKID: 'invalid'}}; - } - } - config.setConfig({ - userSync: { - auctionDelay: 0, - userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} - }] - } - }); - const consent = {given: true}; - gdprDataHandler.setConsentData(consent); - return expectImmediateBidHook(auctionSpy, {adUnits}).then(() => { - expect(adUnits[0].bids[0].userId.mid).to.eql('valid'); - }) - }) - it('delays auction if auctionDelay is set, timing out at auction delay', function () { config.setConfig({ userSync: { diff --git a/test/spec/unit/core/consentHandler_spec.js b/test/spec/unit/core/consentHandler_spec.js index 9d5170eb667..082ff34f90c 100644 --- a/test/spec/unit/core/consentHandler_spec.js +++ b/test/spec/unit/core/consentHandler_spec.js @@ -56,38 +56,4 @@ describe('Consent data handler', () => { }) }) }); - - it('should run onConsentChange listeners when consent data changes', () => { - handler.setConsentData({consent: 'data'}); - const listener = sinon.stub(); - handler.onConsentChange(listener); - handler.setConsentData({consent: 'data'}); - sinon.assert.notCalled(listener); - const newConsent = {other: 'data'}; - handler.setConsentData(newConsent); - sinon.assert.calledWith(listener, newConsent); - }); - - it('should not choke if listener throws', () => { - handler.onConsentChange(sinon.stub().throws(new Error())); - const listener = sinon.stub(); - handler.onConsentChange(listener); - const consent = {consent: 'data'}; - handler.setConsentData(consent); - sinon.assert.calledWith(listener, consent); - }); - - Object.entries({ - 'undefined': undefined, - 'null': null - }).forEach(([t, val]) => { - it(`should run onConsentChange when consent is first set to ${t}`, () => { - const listener = sinon.stub(); - handler.onConsentChange(listener); - handler.setConsentData(val); - handler.setConsentData(val); - sinon.assert.calledOnce(listener); - sinon.assert.calledWith(listener, val); - }) - }) }) From a64c8c0a2b549ce2daeac231ecaaced85633301e Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Thu, 10 Nov 2022 12:52:40 +0530 Subject: [PATCH 426/569] PubMatic Bid Adapter: convert bid cpm to float (#9219) * Convert cpm to float * modified test case Co-authored-by: pm-azhar-mulla --- modules/pubmaticBidAdapter.js | 2 +- test/spec/modules/pubmaticBidAdapter_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 9e2d2bc55f8..296a4314ac8 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1254,7 +1254,7 @@ export const spec = { seatbidder.bid.forEach(bid => { let newBid = { requestId: bid.impid, - cpm: (parseFloat(bid.price) || 0).toFixed(2), + cpm: parseFloat((bid.price || 0).toFixed(2)), width: bid.w, height: bid.h, creativeId: bid.crid || bid.id, diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 13b4e31b5c7..6b240cb2d06 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -3406,7 +3406,7 @@ describe('PubMatic adapter', function () { let response = spec.interpretResponse(bidResponses, request); expect(response).to.be.an('array').with.length.above(0); expect(response[0].requestId).to.equal(bidResponses.body.seatbid[0].bid[0].impid); - expect(response[0].cpm).to.equal((bidResponses.body.seatbid[0].bid[0].price).toFixed(2)); + expect(response[0].cpm).to.equal(parseFloat((bidResponses.body.seatbid[0].bid[0].price).toFixed(2))); expect(response[0].width).to.equal(bidResponses.body.seatbid[0].bid[0].w); expect(response[0].height).to.equal(bidResponses.body.seatbid[0].bid[0].h); if (bidResponses.body.seatbid[0].bid[0].crid) { @@ -3430,7 +3430,7 @@ describe('PubMatic adapter', function () { expect(response[0].partnerImpId).to.equal(bidResponses.body.seatbid[0].bid[0].id); expect(response[1].requestId).to.equal(bidResponses.body.seatbid[1].bid[0].impid); - expect(response[1].cpm).to.equal((bidResponses.body.seatbid[1].bid[0].price).toFixed(2)); + expect(response[1].cpm).to.equal(parseFloat((bidResponses.body.seatbid[1].bid[0].price).toFixed(2))); expect(response[1].width).to.equal(bidResponses.body.seatbid[1].bid[0].w); expect(response[1].height).to.equal(bidResponses.body.seatbid[1].bid[0].h); if (bidResponses.body.seatbid[1].bid[0].crid) { From 4082e8ad23a99ed23f4775322988d21afca5de34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Thu, 10 Nov 2022 09:51:40 +0200 Subject: [PATCH 427/569] Vidazoo Bid Adapter: cache optimizations (#9209) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * feat(module): vidazoo bid adapter - added performance optimization Co-authored-by: roman Co-authored-by: Saar Amrani <89377180+saar120@users.noreply.github.com> Co-authored-by: Saar Amrani --- modules/vidazooBidAdapter.js | 15 ++++++++++++++- test/spec/modules/vidazooBidAdapter_spec.js | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 195851ad23a..fa44bde74f1 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -12,6 +12,7 @@ const TTL_SECONDS = 60 * 5; const DEAL_ID_EXPIRY = 1000 * 60 * 15; const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 60; const SESSION_ID_KEY = 'vidSid'; +const OPT_CACHE_KEY = 'vdzwopt'; export const SUPPORTED_ID_SYSTEMS = { 'britepoolid': 1, 'criteoId': 1, @@ -66,6 +67,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const cId = extractCID(params); const pId = extractPID(params); const subDomain = extractSubDomain(params); + const ptrace = getCacheOpt(); let data = { url: encodeURIComponent(topWindowUrl), @@ -83,7 +85,8 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { bidderVersion: BIDDER_VERSION, prebidVersion: '$prebid.version$', res: `${screen.width}x${screen.height}`, - schain: schain + schain: schain, + ptrace: ptrace }; appendUserIdsToRequestPayload(data, userId); @@ -258,6 +261,16 @@ export function getVidazooSessionId() { return getStorageItem(SESSION_ID_KEY) || ''; } +export function getCacheOpt() { + let data = storage.getDataFromLocalStorage(OPT_CACHE_KEY); + if (!data) { + data = String(Date.now()); + storage.setDataInLocalStorage(OPT_CACHE_KEY, data); + } + + return data; +} + export function getStorageItem(key) { try { return tryParseJSON(storage.getDataFromLocalStorage(key)); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index d30b9f31417..856ceaf438d 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -182,6 +182,7 @@ describe('VidazooBidAdapter', function () { bidderVersion: adapter.version, prebidVersion: version, schain: BID.schain, + ptrace: '1000', res: `${window.top.screen.width}x${window.top.screen.height}`, uqs: getTopWindowQueryParams(), 'ext.param1': 'loremipsum', From 31b070cfb71394dd2c3a00c774af55d7f255a741 Mon Sep 17 00:00:00 2001 From: azuryo <108641852+azuryo@users.noreply.github.com> Date: Thu, 10 Nov 2022 23:20:56 +0900 Subject: [PATCH 428/569] Microad Bid Adapter: Support Audience ID Ext and UID2.0 (#9157) * Microad Bid Adapter: Support ext params * Microad Bid Adapter: Support Unified ID 2.0 * Microad bid Adapter: Add source --- modules/microadBidAdapter.js | 33 +++++++++----- test/spec/modules/microadBidAdapter_spec.js | 48 +++++++++++++++++++++ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index 1b31000df43..13aea0f3f77 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -1,4 +1,5 @@ -import { deepAccess, isEmpty, isStr } from '../src/utils.js'; +import { deepAccess, isArray, isEmpty, isStr } from '../src/utils.js'; +import { find } from '../src/polyfill.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; @@ -24,15 +25,16 @@ const NATIVE_CODE = 2; const VIDEO_CODE = 4; const AUDIENCE_IDS = [ - {type: 6, bidKey: 'userId.imuid'}, - {type: 8, bidKey: 'userId.id5id.uid'}, - {type: 9, bidKey: 'userId.tdid'}, - {type: 10, bidKey: 'userId.novatiq.snowflake'}, - {type: 11, bidKey: 'userId.parrableId.eid'}, - {type: 12, bidKey: 'userId.dacId.id'}, - {type: 13, bidKey: 'userId.idl_env'}, - {type: 14, bidKey: 'userId.criteoId'}, - {type: 15, bidKey: 'userId.pubcid'} + {type: 6, bidKey: 'userId.imuid', source: 'intimatemerger.com'}, + {type: 8, bidKey: 'userId.id5id.uid', source: 'id5-sync.com'}, + {type: 9, bidKey: 'userId.tdid', source: 'adserver.org'}, + {type: 10, bidKey: 'userId.novatiq.snowflake', source: 'novatiq.com'}, + {type: 11, bidKey: 'userId.parrableId.eid', source: 'parrable.com'}, + {type: 12, bidKey: 'userId.dacId.id', source: 'dac.co.jp'}, + {type: 13, bidKey: 'userId.idl_env', source: 'liveramp.com'}, + {type: 14, bidKey: 'userId.criteoId', source: 'criteo.com'}, + {type: 15, bidKey: 'userId.pubcid', source: 'pubcid.org'}, + {type: 17, bidKey: 'userId.uid2.id', source: 'uidapi.com'} ]; function createCBT() { @@ -100,10 +102,19 @@ export const spec = { } const aidsParams = [] + const userIdAsEids = bid.userIdAsEids; AUDIENCE_IDS.forEach((audienceId) => { const bidAudienceId = deepAccess(bid, audienceId.bidKey); if (!isEmpty(bidAudienceId) && isStr(bidAudienceId)) { - aidsParams.push({ type: audienceId.type, id: bidAudienceId }); + const aidParam = { type: audienceId.type, id: bidAudienceId }; + // Set ext + if (isArray(userIdAsEids)) { + const targetEid = find(userIdAsEids, (eid) => eid.source === audienceId.source) || {}; + if (!isEmpty(deepAccess(targetEid, 'uids.0.ext'))) { + aidParam.ext = targetEid.uids[0].ext; + } + } + aidsParams.push(aidParam); // Set Ramp ID if (audienceId.type === 13) params['idl_env'] = bidAudienceId; } diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js index cca337c83f2..98edfce20bc 100644 --- a/test/spec/modules/microadBidAdapter_spec.js +++ b/test/spec/modules/microadBidAdapter_spec.js @@ -330,6 +330,54 @@ describe('microadBidAdapter', () => { }) }) }) + + Object.entries({ + 'ID5 ID': { + userId: {id5id: {uid: 'id5id-sample'}}, + userIdAsEids: [ + { + source: 'id5-sync.com', + uids: [{id: 'id5id-sample', aType: 1, ext: {linkType: 2, abTestingControlGroup: false}}] + } + ], + expected: { + aids: JSON.stringify([{type: 8, id: 'id5id-sample', ext: {linkType: 2, abTestingControlGroup: false}}]) + } + }, + 'Unified ID': { + userId: {tdid: 'unified-sample'}, + userIdAsEids: [ + { + source: 'adserver.org', + uids: [{id: 'unified-sample', aType: 1, ext: {rtiPartner: 'TDID'}}] + } + ], + expected: {aids: JSON.stringify([{type: 9, id: 'unified-sample', ext: {rtiPartner: 'TDID'}}])} + }, + 'not add': { + userId: {id5id: {uid: 'id5id-sample'}}, + userIdAsEids: [], + expected: { + aids: JSON.stringify([{type: 8, id: 'id5id-sample'}]) + } + } + }).forEach(([test, arg]) => { + it(`should ${test} ext if it is available in request parameters`, () => { + const bidRequestWithUserId = { + ...bidRequestTemplate, + userId: arg.userId, + userIdAsEids: arg.userIdAsEids + } + const requests = spec.buildRequests([bidRequestWithUserId], bidderRequest) + requests.forEach((request) => { + expect(request.data).to.deep.equal({ + ...expectedResultTemplate, + cbt: request.data.cbt, + ...arg.expected + }) + }) + }); + }) }); describe('interpretResponse', () => { From db0c7f9993911d5808d348b07eb22e822d807db5 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 10 Nov 2022 21:23:46 +0000 Subject: [PATCH 429/569] Prebid 7.25.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 48ef496ee8e..7c8b7ac89b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.25.0-pre", + "version": "7.25.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index d98f313f4dc..685e9bd892d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.25.0-pre", + "version": "7.25.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 056ec5e1f7df55f412e0d040270e77f859a32ae3 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 10 Nov 2022 21:23:46 +0000 Subject: [PATCH 430/569] Increment version to 7.26.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c8b7ac89b8..462484cb962 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.25.0", + "version": "7.26.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 685e9bd892d..8df1268bbd2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.25.0", + "version": "7.26.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 25b46f92490d5e13376632836020cebdbdd1e459 Mon Sep 17 00:00:00 2001 From: nkloeber <100145701+nkloeber@users.noreply.github.com> Date: Fri, 11 Nov 2022 14:25:25 +0100 Subject: [PATCH 431/569] Add support for additional content signals under the iab_content (#9235) --- modules/yieldlabBidAdapter.js | 27 ++-- test/spec/modules/yieldlabBidAdapter_spec.js | 125 +++++++++++++++++++ 2 files changed, 144 insertions(+), 8 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index e5e452b4ce0..e6758e78022 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -370,21 +370,32 @@ function getContentObject(bid) { } /** - * Creates a string for iab_content object + * Creates a string for iab_content object by + * 1. flatten the iab content object + * 2. encoding the values + * 3. joining array of defined keys ('keyword', 'cat') into one value seperated with '|' + * 4. encoding the whole string * @param {Object} iabContent * @returns {String} */ function createIabContentString(iabContent) { const arrKeys = ['keywords', 'cat'] const str = [] - for (const key in iabContent) { - if (iabContent.hasOwnProperty(key)) { - const value = (arrKeys.indexOf(key) !== -1 && Array.isArray(iabContent[key])) - ? iabContent[key].map(node => encodeURIComponent(node)).join('|') : encodeURIComponent(iabContent[key]) - str.push(''.concat(key, ':', value)) + const transformObjToParam = (obj = {}, extraKey = '') => { + for (const key in obj) { + if ((arrKeys.indexOf(key) !== -1 && Array.isArray(obj[key]))) { + // Array of defined keyword which have to be joined into one value from "key: [value1, value2, value3]" to "key:value1|value2|value3" + str.push(''.concat(key, ':', obj[key].map(node => encodeURIComponent(node)).join('|'))) + } else if (typeof obj[key] !== 'object') { + str.push(''.concat(extraKey + key, ':', encodeURIComponent(obj[key]))) + } else { + // Object has to be further flattened + transformObjToParam(obj[key], ''.concat(extraKey, key, '.')); + } } - } - return encodeURIComponent(str.join(',')) + return str.join(','); + }; + return encodeURIComponent(transformObjToParam(iabContent)) } /** diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 4c39fe5fe29..9155884106c 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -80,6 +80,81 @@ const NATIVE_REQUEST = () => Object.assign(DEFAULT_REQUEST(), { } }) +const IAB_REQUEST = () => Object.assign(DEFAULT_REQUEST(), { + params: { + adslotId: '1111', + supplyId: '2222', + iabContent: { + id: 'foo', + episode: '99', + title: 'bar', + series: 'baz', + season: 's01', + artist: 'foobar', + genre: 'barbaz', + isrc: 'CC-XXX-YY-NNNNN', + url: 'https://foo.test', + cat: ['cat1', 'cat2,ppp', 'cat3|||//'], + context: '2', + keywords: ['k1', 'k2', 'k3', 'k4'], + live: '0', + album: 'foo', + cattax: '3', + prodq: 2, + contentrating: 'foo', + userrating: 'bar', + qagmediarating: 2, + sourcerelationship: 1, + len: 12345, + language: 'en', + embeddable: 0, + producer: { + id: 'foo', + name: 'bar', + cattax: 532, + cat: [1, 'foo', true], + domain: 'producer.test' + }, + data: { + id: 'foo', + name: 'bar', + segment: [{ + name: 'foo', + value: 'bar', + ext: { + foo: { + bar: 'bar' + } + }, + }, { + name: 'foo2', + value: 'bar2', + ext: { + test: { + nums: { + int: 123, + float: 123.123 + }, + bool: true, + string: 'foo2' + } + } + }], + }, + network: { + id: 'foo', + name: 'bar', + domain: 'network.test' + }, + channel: { + id: 'bar', + name: 'foo', + domain: 'channel.test' + } + } + } +}) + const RESPONSE = { advertiser: 'yieldlab', curl: 'https://www.yieldlab.de', @@ -251,6 +326,56 @@ describe('yieldlabBidAdapter', () => { const request = spec.buildRequests([{...requestWithoutIabContent, ...siteConfig}]) expect(request.url).to.include('iab_content=id%3Aid_from_config') }) + + it('flattens the iabContent, encodes the values, joins the keywords into one value, and than encodes the iab_content request param ', () => { + const expectedIabContentValue = encodeURIComponent( + 'id:foo,' + + 'episode:99,' + + 'title:bar,' + + 'series:baz,' + + 'season:s01,' + + 'artist:foobar,' + + 'genre:barbaz,' + + 'isrc:CC-XXX-YY-NNNNN,' + + 'url:https%3A%2F%2Ffoo.test,' + + 'cat:cat1|cat2%2Cppp|cat3%7C%7C%7C%2F%2F,' + + 'context:2,' + + 'keywords:k1|k2|k3|k4,' + + 'live:0,' + + 'album:foo,' + + 'cattax:3,' + + 'prodq:2,' + + 'contentrating:foo,' + + 'userrating:bar,' + + 'qagmediarating:2,' + + 'sourcerelationship:1,' + + 'len:12345,' + + 'language:en,' + + 'embeddable:0,' + + 'producer.id:foo,' + + 'producer.name:bar,' + + 'producer.cattax:532,' + + 'cat:1|foo|true,' + + 'producer.domain:producer.test,' + + 'data.id:foo,data.name:bar,' + + 'data.segment.0.name:foo,' + + 'data.segment.0.value:bar,' + + 'data.segment.0.ext.foo.bar:bar,' + + 'data.segment.1.name:foo2,' + + 'data.segment.1.value:bar2,' + + 'data.segment.1.ext.test.nums.int:123,' + + 'data.segment.1.ext.test.nums.float:123.123,' + + 'data.segment.1.ext.test.bool:true,' + + 'data.segment.1.ext.test.string:foo2,' + + 'network.id:foo,network.name:bar,' + + 'network.domain:network.test,' + + 'channel.id:bar,' + + 'channel.name:foo,' + + 'channel.domain:channel.test' + ) + const request = spec.buildRequests([IAB_REQUEST()], REQPARAMS) + expect(request.url).to.include('iab_content=' + expectedIabContentValue) + }) }) it('passes unencoded schain string to bid request when complete == 0', () => { From 06ce1ab1b53b38de89bdd641b4147ae146c9b785 Mon Sep 17 00:00:00 2001 From: preved-medved Date: Mon, 14 Nov 2022 11:51:02 +0000 Subject: [PATCH 432/569] SmartyTech Bid Adapter : initial adapter release (#9196) * Add new bid adapter for company smartytech * change domain to prod * update unit tests * remove unused code * remove unused code --- modules/smartytechBidAdapter.js | 78 +++++++++ modules/smartytechBidAdapter.md | 44 +++++ .../spec/modules/smartytechBidAdapter_spec.js | 162 ++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 modules/smartytechBidAdapter.js create mode 100644 modules/smartytechBidAdapter.md create mode 100644 test/spec/modules/smartytechBidAdapter_spec.js diff --git a/modules/smartytechBidAdapter.js b/modules/smartytechBidAdapter.js new file mode 100644 index 00000000000..231ca315de8 --- /dev/null +++ b/modules/smartytechBidAdapter.js @@ -0,0 +1,78 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {buildUrl} from '../src/utils.js' + +const BIDDER_CODE = 'smartytech'; +export const ENDPOINT_PROTOCOL = 'https'; +export const ENDPOINT_DOMAIN = 'server.smartytech.io'; +export const ENDPOINT_PATH = '/hb/bidder'; + +export const spec = { + code: BIDDER_CODE, + + isBidRequestValid: function (bidRequest) { + return !!parseInt(bidRequest.params.endpointId); + }, + + buildRequests: function (validBidRequests, bidderRequest) { + const referer = bidderRequest?.refererInfo?.page || window.location.href; + + const bidRequests = validBidRequests.map((validBidRequest) => { + return { + endpointId: validBidRequest.params.endpointId, + adUnitCode: validBidRequest.adUnitCode, + sizes: validBidRequest.sizes, + bidId: validBidRequest.bidId, + referer: referer + }; + }); + + let adPartnerRequestUrl = buildUrl({ + protocol: ENDPOINT_PROTOCOL, + hostname: ENDPOINT_DOMAIN, + pathname: ENDPOINT_PATH, + }); + + return { + method: 'POST', + url: adPartnerRequestUrl, + data: bidRequests + }; + }, + + interpretResponse: function (serverResponse, bidRequest) { + if (typeof serverResponse.body === 'undefined') { + return []; + } + + const validBids = bidRequest.data; + const keys = Object.keys(serverResponse.body) + const responseBody = serverResponse.body; + + return keys.filter(key => { + return responseBody[key].ad + }).map(key => { + return { + bid: validBids.find(b => b.adUnitCode === key), + response: responseBody[key] + } + }).map(item => spec.adResponse(item.bid.bidId, item.response)); + }, + + adResponse: function (requestId, response) { + const bidObject = { + requestId, + ad: response.ad, + cpm: response.cpm, + width: response.width, + height: response.height, + ttl: 60, + creativeId: response.creativeId, + netRevenue: true, + currency: response.currency, + } + return bidObject; + }, + +} + +registerBidder(spec); diff --git a/modules/smartytechBidAdapter.md b/modules/smartytechBidAdapter.md new file mode 100644 index 00000000000..dbfc2833c78 --- /dev/null +++ b/modules/smartytechBidAdapter.md @@ -0,0 +1,44 @@ +# Overview + +Module Name: SmartyTech Bidder Adapter + +Module Type: Bidder Adapter + +Maintainer: info@adpartner.pro + +# Description + +You can use this adapter to get a bid from smartytech.io. + +About us : https://smartytech.io + +# Test Parameters + +```javascript + var adUnits = [ + { + code: 'div-smartytech-example', + sizes: [[300, 250]], + bids: [ + { + bidder: "smartytech", + params: { + endpointId: 14 + } + } + ] + }, + { + code: 'div-smartytech-example-2', + sizes: [[300, 250]], + bids: [ + { + bidder: "smartytech", + params: { + endpointId: 14 + } + } + ] + } +]; +``` diff --git a/test/spec/modules/smartytechBidAdapter_spec.js b/test/spec/modules/smartytechBidAdapter_spec.js new file mode 100644 index 00000000000..78503d7a6f0 --- /dev/null +++ b/test/spec/modules/smartytechBidAdapter_spec.js @@ -0,0 +1,162 @@ +import {expect} from 'chai'; +import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH} from 'modules/smartytechBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'smartytech'; + +describe('SmartyTechDSPAdapter: inherited functions', function () { + let adapter; + beforeEach(() => { + adapter = newBidder(spec); + }); + it('exists and is a function', function () { + expect(adapter.callBids).to.be.exist.and.to.be.a('function'); + }); + it(`bidder code is ${BIDDER_CODE}`, function () { + expect(spec.code).to.be.equal(BIDDER_CODE); + }) +}); + +describe('SmartyTechDSPAdapter: isBidRequestValid', function () { + it('Invalid bid request. Should return false', function () { + const invalidBidFixture = { + params: { + use_id: 13144375 + } + } + expect(spec.isBidRequestValid(invalidBidFixture)).to.be.false + }); + it('Valid bid request. Should return true', function () { + const validBidFixture = { + params: { + endpointId: 13144375 + } + } + expect(spec.isBidRequestValid(validBidFixture)).to.be.true + }); +}); + +function mockRandomSizeArray(len) { + return Array.apply(null, {length: len}).map(i => { + return [Math.floor(Math.random() * 800), Math.floor(Math.random() * 800)] + }); +} + +function mockBidRequestListData(size) { + return Array.apply(null, {length: size}).map((i, index) => { + const id = Math.floor(Math.random() * 800) * (index + 1); + return { + adUnitCode: `adUnitCode-${id}`, + sizes: mockRandomSizeArray(index + 1), + bidId: `bidId-${id}`, + params: { + endpointId: id + } + } + }); +} + +function mockRefererData() { + return { + refererInfo: { + page: 'https://some-test.page' + } + } +} + +function mockResponseData(requestData) { + let data = {} + + requestData.data.forEach((request, index) => { + const sizeArrayIndex = Math.floor(Math.random() * (request.sizes.length - 1)); + const rndIndex = Math.floor(Math.random() * 800); + + data[request.adUnitCode] = { + ad: `ad-${rndIndex}`, + width: request.sizes[sizeArrayIndex][0], + height: request.sizes[sizeArrayIndex][1], + creativeId: `creative-id-${index}`, + cpm: Math.floor(Math.random() * 100), + currency: `UAH-${rndIndex}` + }; + }); + return { + body: data + } +}; + +describe('SmartyTechDSPAdapter: buildRequests', () => { + let mockBidRequest; + let mockReferer; + beforeEach(() => { + mockBidRequest = mockBidRequestListData(8); + mockReferer = mockRefererData(); + }); + it('has return data', () => { + const request = spec.buildRequests(mockBidRequest, mockReferer); + expect(request).not.null; + }); + it('correct request URL', () => { + const request = spec.buildRequests(mockBidRequest, mockReferer); + expect(request.url).to.be.equal(`${ENDPOINT_PROTOCOL}://${ENDPOINT_DOMAIN}${ENDPOINT_PATH}`) + }); + it('correct request method', () => { + const request = spec.buildRequests(mockBidRequest, mockReferer); + expect(request.method).to.be.equal(`POST`) + }); + it('correct request data', () => { + const data = spec.buildRequests(mockBidRequest, mockReferer).data; + data.forEach((request, index) => { + expect(request.adUnitCode).to.be.equal(mockBidRequest[index].adUnitCode); + expect(request.sizes).to.be.equal(mockBidRequest[index].sizes); + expect(request.bidId).to.be.equal(mockBidRequest[index].bidId); + expect(request.endpointId).to.be.equal(mockBidRequest[index].params.endpointId); + expect(request.referer).to.be.equal(mockReferer.refererInfo.page); + }) + }); +}); + +describe('SmartyTechDSPAdapter: interpretResponse', () => { + let mockBidRequest; + let mockReferer; + let request; + let mockResponse; + beforeEach(() => { + const brData = mockBidRequestListData(2); + mockReferer = mockRefererData(); + request = spec.buildRequests(brData, mockReferer); + mockBidRequest = { + data: brData + } + mockResponse = mockResponseData(request); + }); + + it('interpretResponse: empty data request', () => { + delete mockResponse['body'] + const data = spec.interpretResponse(mockResponse, mockBidRequest); + expect(data.length).to.be.equal(0); + }); + + it('interpretResponse: response data and convert data arrays has same length', () => { + const keys = Object.keys(mockResponse.body); + const data = spec.interpretResponse(mockResponse, mockBidRequest); + expect(data.length).to.be.equal(keys.length); + }); + + it('interpretResponse: convert to correct data', () => { + const keys = Object.keys(mockResponse.body); + const data = spec.interpretResponse(mockResponse, mockBidRequest); + + data.forEach((responseItem, index) => { + expect(responseItem.ad).to.be.equal(mockResponse.body[keys[index]].ad); + expect(responseItem.cpm).to.be.equal(mockResponse.body[keys[index]].cpm); + expect(responseItem.creativeId).to.be.equal(mockResponse.body[keys[index]].creativeId); + expect(responseItem.currency).to.be.equal(mockResponse.body[keys[index]].currency); + expect(responseItem.netRevenue).to.be.true; + expect(responseItem.ttl).to.be.equal(60); + expect(responseItem.requestId).to.be.equal(mockBidRequest.data[index].bidId); + expect(responseItem.width).to.be.equal(mockResponse.body[keys[index]].width); + expect(responseItem.height).to.be.equal(mockResponse.body[keys[index]].height); + }); + }); +}); From 12cad7b43f1458121ac574d2a657af49f8c96535 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Mon, 14 Nov 2022 15:30:14 +0100 Subject: [PATCH 433/569] ZetaSppBidAdapter: provide tmax (#9240) Co-authored-by: Surovenko Alexey --- modules/zeta_global_sspBidAdapter.js | 4 ++++ .../modules/zeta_global_sspBidAdapter_spec.js | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 4689683fbc7..6c5b9783782 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -129,6 +129,10 @@ export const spec = { deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + if (bidderRequest?.timeout) { + payload.tmax = bidderRequest.timeout; + } + provideEids(validBidRequests[0], payload); const url = params.shortname ? ENDPOINT_URL.concat('?shortname=', params.shortname) : ENDPOINT_URL; return { diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index 3bd17697f2d..d6befa0fc78 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -104,7 +104,8 @@ describe('Zeta Ssp Bid Adapter', function () { }, uspConsent: 'someCCPAString', params: params, - userIdAsEids: eids + userIdAsEids: eids, + timeout: 500 }]; const videoRequest = [{ @@ -344,4 +345,18 @@ describe('Zeta Ssp Bid Adapter', function () { expect(payload.imp[1].banner.w).to.eql(600); expect(payload.imp[1].banner.h).to.eql(400); }); + + it('Test provide tmax', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + + expect(payload.tmax).to.eql(500); + }); + + it('Test provide tmax without value', function () { + const request = spec.buildRequests(videoRequest, videoRequest[0]); + const payload = JSON.parse(request.data); + + expect(payload.tmax).to.be.undefined; + }); }); From 0470f7085b6267b52dd99aa1b87c386e17bbc2c9 Mon Sep 17 00:00:00 2001 From: Fatih Kaya Date: Mon, 14 Nov 2022 17:51:24 +0300 Subject: [PATCH 434/569] Admatic Bid Adapter: pixad alias and bid floor features activated (#9204) * Admatic Bidder Adaptor * Update admaticBidAdapter.md * Update admaticBidAdapter.md * remove floor parameter * Update admaticBidAdapter.js * Admatic Bid Adapter: alias and bid floor features activated * Admatic adapter: host param control changed * Alias name changed. * Revert "Admatic adapter: host param control changed" This reverts commit de7ac85981b1ba3ad8c5d1dc95c5dadbdf5b9895. * added alias feature and host param * Revert "added alias feature and host param" This reverts commit 6ec8f4539ea6be403a0d7e08dad5c7a5228f28a1. * Revert "Alias name changed." This reverts commit 661c54f9b2397e8f25c257144d73161e13466281. * Revert "Admatic Bid Adapter: alias and bid floor features activated" This reverts commit 7a2e0e29c49e2f876b68aafe886b336fe2fe6fcb. * Revert "Update admaticBidAdapter.js" This reverts commit 7a845b7151bbb08addfb58ea9bd5b44167cc8a4e. * Revert "remove floor parameter" This reverts commit 7a23b055ccd4ea23d23e73248e82b21bc6f69d90. * Admatic adapter: host param control && Add new Bidder * Revert "Admatic adapter: host param control && Add new Bidder" This reverts commit 3c797b120c8e0fe2b851381300ac5c4b1f92c6e2. * commit new features * Update admaticBidAdapter.js * updated for coverage * sync updated * Update adloader.js --- modules/admaticBidAdapter.js | 167 +++++++----- modules/admaticBidAdapter.md | 4 +- src/adloader.js | 1 - test/spec/modules/admaticBidAdapter_spec.js | 265 +++++++++++++++++++- 4 files changed, 372 insertions(+), 65 deletions(-) diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js index 039f27e5de0..ec4401b77c9 100644 --- a/modules/admaticBidAdapter.js +++ b/modules/admaticBidAdapter.js @@ -1,94 +1,93 @@ import { getValue, logError, deepAccess, getBidIdParameter, isArray } from '../src/utils.js'; -import { loadExternalScript } from '../src/adloader.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const ENDPOINT_URL = 'https://layer.serve.admatic.com.tr/pb'; -const SYNC_URL = 'https://cdn.serve.admatic.com.tr/showad/sync.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +const SYNC_URL = 'https://cdn.serve.admatic.com.tr/showad/sync.html'; const BIDDER_CODE = 'admatic'; - export const spec = { - code: 'admatic', - supportedMediaTypes: ['video', 'banner'], - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. + code: BIDDER_CODE, + aliases: [ + {code: 'pixad'} + ], + supportedMediaTypes: [BANNER, VIDEO], + /** f + * @param {object} bid + * @return {boolean} */ - isBidRequestValid: function(bid) { + isBidRequestValid: (bid) => { let isValid = false; - if (typeof bid.params !== 'undefined') { - let isValidNetworkId = _validateId(getValue(bid.params, 'networkId')); - isValid = isValidNetworkId;// && isValidTypeId; + if (bid?.params) { + const isValidNetworkId = _validateId(getValue(bid.params, 'networkId')); + const isValidHost = _validateString(getValue(bid.params, 'host')); + isValid = isValidNetworkId && isValidHost; } if (!isValid) { - logError('AdMatic networkId parameters are required. Bid aborted.'); + logError(`${bid.bidder} networkId and host parameters are required. Bid aborted.`); } return isValid; }, + /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} an array of bids - * @return ServerRequest Info describing the request to the server. + * @param {BidRequest[]} validBidRequests + * @return {ServerRequest} */ - buildRequests: function(validBidRequests, bidderRequest) { + buildRequests: (validBidRequests, bidderRequest) => { const bids = validBidRequests.map(buildRequestObject); const networkId = getValue(validBidRequests[0].params, 'networkId'); + const host = getValue(validBidRequests[0].params, 'host'); const currency = getValue(validBidRequests[0].params, 'currency') || 'TRY'; - - setTimeout(() => { - loadExternalScript(SYNC_URL, BIDDER_CODE); - }, bidderRequest.timeout); + const bidderName = validBidRequests[0].bidder; const payload = { - 'user': { - 'ua': navigator.userAgent + user: { + ua: navigator.userAgent }, - 'blacklist': [], - 'site': { - 'page': location.href, - 'ref': location.origin, - 'publisher': { - 'name': location.hostname, - 'publisherId': networkId + blacklist: [], + site: { + page: location.href, + ref: location.origin, + publisher: { + name: location.hostname, + publisherId: networkId } }, imp: bids, ext: { - 'cur': currency, - 'type': 'admatic' + cur: currency, + bidder: bidderName } }; - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: ENDPOINT_URL, - data: payloadString, - options: { - contentType: 'application/json' - } - }; + if (payload) { + return { method: 'POST', url: `https://${host}/pb?bidder=${bidderName}`, data: payload, options: { contentType: 'application/json' } }; + } + }, + + getUserSyncs: function (syncOptions, responses) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: SYNC_URL + }]; + } }, + /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. + * @param {*} response + * @param {ServerRequest} request + * @return {Bid[]} */ interpretResponse: (response, request) => { const body = response.body || response; const bidResponses = []; - if (body.data.length > 0) { - body.data.forEach(function (bid) { + if (body && body?.data && isArray(body.data)) { + body.data.forEach(bid => { const resbid = { requestId: bid.id, cpm: bid.price, width: bid.width, height: bid.height, - currency: body.cur, + currency: body.cur || 'TRY', netRevenue: true, ad: bid.party_tag, creativeId: bid.creative_id, @@ -96,22 +95,68 @@ export const spec = { advertiserDomains: bid && bid.adomain ? bid.adomain : [] }, ttl: 360, - bidder: 'admatic', - timeToRespond: 1, - requestTimestamp: 1 + bidder: bid.bidder }; + bidResponses.push(resbid); }); - }; + } return bidResponses; } }; +function enrichSlotWithFloors(slot, bidRequest) { + try { + const slotFloors = {}; + + if (bidRequest.getFloor) { + if (bidRequest.mediaTypes?.banner) { + slotFloors.banner = {}; + const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')) + bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({ size: bannerSize, mediaType: BANNER })); + } + + if (bidRequest.mediaTypes?.video) { + slotFloors.video = {}; + const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) + videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({ size: videoSize, mediaType: VIDEO })); + } + + if (Object.keys(slotFloors).length > 0) { + if (!slot) { + slot = {} + } + Object.assign(slot, { + floors: slotFloors + }); + } + } + } catch (e) { + logError('Could not parse floors from Prebid: ' + e); + } +} + +function parseSizes(sizes, parser = s => s) { + if (sizes == undefined) { + return []; + } + if (Array.isArray(sizes[0])) { // is there several sizes ? (ie. [[728,90],[200,300]]) + return sizes.map(size => parser(size)); + } + return [parser(sizes)]; // or a single one ? (ie. [728,90]) +} + +function parseSize(size) { + return size[0] + 'x' + size[1]; +} + function buildRequestObject(bid) { const reqObj = {}; reqObj.size = getSizes(bid); reqObj.id = getBidIdParameter('bidId', bid); - reqObj.floor = getValue(bid.params, 'floor') || 0.01; + + enrichSlotWithFloors(reqObj, bid); + return reqObj; } @@ -144,4 +189,8 @@ function _validateId(id) { return (parseInt(id) > 0); } +function _validateString(str) { + return (typeof str == 'string'); +} + registerBidder(spec); diff --git a/modules/admaticBidAdapter.md b/modules/admaticBidAdapter.md index de84998d0af..2bf9afb3cdc 100644 --- a/modules/admaticBidAdapter.md +++ b/modules/admaticBidAdapter.md @@ -19,7 +19,7 @@ Use `admatic` as bidder. bidder: 'admatic', params: { networkId: 12345, - floor: 0.5 + host: 'layer.serve.admatic.com.tr' } }] },{ @@ -29,7 +29,7 @@ Use `admatic` as bidder. bidder: 'admatic', params: { networkId: 12345, - floor: 0.5 + host: 'layer.serve.admatic.com.tr' } }] }]; diff --git a/src/adloader.js b/src/adloader.js index 330b52b3ed5..64408683e9f 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -6,7 +6,6 @@ const _requestCache = new WeakMap(); const _approvedLoadExternalJSList = [ 'debugging', 'adloox', - 'admatic', 'criteo', 'outstream', 'adagio', diff --git a/test/spec/modules/admaticBidAdapter_spec.js b/test/spec/modules/admaticBidAdapter_spec.js index c7d391cfaca..65a7b4111b7 100644 --- a/test/spec/modules/admaticBidAdapter_spec.js +++ b/test/spec/modules/admaticBidAdapter_spec.js @@ -3,7 +3,7 @@ import {spec, storage} from 'modules/admaticBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; import {getStorageManager} from 'src/storageManager'; -const ENDPOINT = 'https://layer.serve.admatic.com.tr/v1'; +const ENDPOINT = 'https://layer.serve.admatic.com.tr/pb?bidder=admatic'; describe('admaticBidAdapter', () => { const adapter = newBidder(spec); @@ -18,7 +18,8 @@ describe('admaticBidAdapter', () => { let bid = { 'bidder': 'admatic', 'params': { - 'networkId': 10433394 + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' }, 'adUnitCode': 'adunit-code', 'sizes': [[300, 250], [300, 600]], @@ -37,10 +38,268 @@ describe('admaticBidAdapter', () => { delete bid.params; bid.params = { - 'networkId': 0 + 'networkId': 0, + 'host': 'layer.serve.admatic.com.tr' }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); + + describe('buildRequests', function () { + it('sends bid request to ENDPOINT via POST', function () { + let validRequest = [ { + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + } + }, + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0 + }; + } else { + return {} + } + }, + 'user': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' + }, + 'blacklist': [], + 'site': { + 'page': 'http://localhost:8888/admatic.html', + 'ref': 'http://localhost:8888', + 'publisher': { + 'name': 'localhost', + 'publisherId': 12321312 + } + }, + 'imp': [ + { + 'size': [ + { + 'w': 300, + 'h': 250 + }, + { + 'w': 728, + 'h': 90 + } + ], + 'id': '2205da7a81846b', + 'floors': { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + } + } + ], + 'ext': { + 'cur': 'USD', + 'bidder': 'admatic' + } + } ]; + let bidderRequest = { + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + } + }, + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0 + }; + } else { + return {} + } + }, + 'user': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' + }, + 'blacklist': [], + 'site': { + 'page': 'http://localhost:8888/admatic.html', + 'ref': 'http://localhost:8888', + 'publisher': { + 'name': 'localhost', + 'publisherId': 12321312 + } + }, + 'imp': [ + { + 'size': [ + { + 'w': 300, + 'h': 250 + }, + { + 'w': 728, + 'h': 90 + } + ], + 'id': '2205da7a81846b', + 'floors': { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + } + } + ], + 'ext': { + 'cur': 'USD', + 'bidder': 'admatic' + } + }; + const request = spec.buildRequests(validRequest, bidderRequest); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should properly build a banner request with floors', function () { + let bidRequests = [ + { + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + } + }, + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0 + }; + } else { + return {} + } + } + }, + ]; + let bidderRequest = { + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [728, 90]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ee', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + } + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + request.data.imp[0].floors = { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + }; + }); + }); + + describe('interpretResponse', function () { + it('should get correct bid responses', function() { + let bids = { body: { + data: [ + { + 'id': 1, + 'creative_id': '374', + 'width': 300, + 'height': 250, + 'price': 0.01, + 'bidder': 'admatic', + 'adomain': ['admatic.com.tr'], + 'party_tag': '
' + } + ], + 'queryId': 'cdnbh24rlv0hhkpfpln0', + 'status': true + }}; + + let expectedResponse = [ + { + requestId: 1, + cpm: 0.01, + width: 300, + height: 250, + currency: 'TRY', + netRevenue: true, + ad: '
', + creativeId: '374', + meta: { + advertiserDomains: ['admatic.com.tr'] + }, + ttl: 360, + bidder: 'admatic' + } + ]; + const request = { + ext: { + 'cur': 'TRY', + 'type': 'admatic' + } + }; + let result = spec.interpretResponse(bids, {data: request}); + expect(result).to.eql(expectedResponse); + }); + + it('handles nobid responses', function () { + let request = { + ext: { + 'cur': 'TRY', + 'type': 'admatic' + } + }; + let bids = { body: { + data: [], + 'queryId': 'cdnbh24rlv0hhkpfpln0', + 'status': true + }}; + + let result = spec.interpretResponse(bids, {data: request}); + expect(result.length).to.equal(0); + }); + }); }); From cc501e167cffb8cb4c839022ad58981acab156d0 Mon Sep 17 00:00:00 2001 From: takuhou Date: Tue, 15 Nov 2022 09:54:03 +0900 Subject: [PATCH 435/569] YieldOne Bid Adapter: update documentation, test and code style (#9233) * add parameter test * change new_format to single_format * fix adomain typo * add parameter test * add jsdoc comment and update readme * change @return to @returns * change param and returns * add typeof and change param/returns * change returns value * change flux to 1x1 Co-authored-by: TakuhoSanpei --- modules/yieldoneBidAdapter.js | 68 +++++++++-- modules/yieldoneBidAdapter.md | 118 +++++++++++++++++-- test/spec/modules/yieldoneBidAdapter_spec.js | 36 +++++- 3 files changed, 199 insertions(+), 23 deletions(-) diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index 4c5f1687641..d408a0595f9 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -4,6 +4,17 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory').BidderSpec} BidderSpec + * @typedef {import('../src/adapters/bidderFactory').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory').UserSync} UserSync + * @typedef {import('../src/auction').BidderRequest} BidderRequest + */ + const BIDDER_CODE = 'yieldone'; const ENDPOINT_URL = 'https://y.one.impact-ad.jp/h_bid'; const USER_SYNC_URL = 'https://y.one.impact-ad.jp/push_sync'; @@ -13,13 +24,25 @@ const VIEWABLE_PERCENTAGE_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstvi const DEFAULT_VIDEO_SIZE = {w: 640, h: 360}; +/** @type BidderSpec */ export const spec = { code: BIDDER_CODE, aliases: ['y1'], supportedMediaTypes: [BANNER, VIDEO], + /** + * Determines whether or not the given bid request is valid. + * @param {BidRequest} bid The bid params to validate. + * @returns {boolean} True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { return !!(bid.params.placementId); }, + /** + * Make a server request from the list of BidRequests. + * @param {Bid[]} validBidRequests - An array of bids. + * @param {BidderRequest} bidderRequest - bidder request object. + * @returns {ServerRequest|ServerRequest[]} ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { return validBidRequests.map(bidRequest => { const params = bidRequest.params; @@ -96,6 +119,12 @@ export const spec = { }; }); }, + /** + * Unpack the response from the server into a list of bids. + * @param {ServerResponse} serverResponse - A successful response from the server. + * @param {BidRequest} bidRequests + * @returns {Bid[]} - An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, bidRequest) { const bidResponses = []; const response = serverResponse.body; @@ -188,6 +217,11 @@ export const spec = { } return bidResponses; }, + /** + * Register the user sync pixels which should be dropped after the auction. + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @returns {UserSync[]} The user syncs which should be dropped. + */ getUserSyncs: function(syncOptions) { if (syncOptions.iframeEnabled) { return [{ @@ -202,7 +236,7 @@ export const spec = { * NOTE: server side does not yet support multiple formats. * @param {Object} bidRequest - * @param {boolean} [enabledOldFormat = true] - default: `true`. - * @return {string|null} - `"banner"` or `"video"` or `null`. + * @returns {string|null} - `"banner"` or `"video"` or `null`. */ function getMediaType(bidRequest, enabledOldFormat = true) { let hasBannerType = Boolean(deepAccess(bidRequest, 'mediaTypes.banner')); @@ -239,7 +273,7 @@ function getMediaType(bidRequest, enabledOldFormat = true) { * @param {Object} bidRequest.banner - * @param {Array} bidRequest.banner.sizes - * @param {boolean} [enabledOldFormat = true] - default: `true`. - * @return {string} - strings like `"300x250"` or `"300x250,728x90"`. + * @returns {string} - strings like `"300x250"` or `"300x250,728x90"`. */ function getBannerSizes(bidRequest, enabledOldFormat = true) { let sizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes'); @@ -254,10 +288,10 @@ function getBannerSizes(bidRequest, enabledOldFormat = true) { /** * @param {Object} bidRequest - * @param {boolean} [enabledOldFormat = true] - default: `true`. - * @param {boolean} [enabledFlux = true] - default: `true`. - * @return {{w: number, h: number}} - + * @param {boolean} [enabled1x1 = true] - default: `true`. + * @returns {{w: number, h: number}} - */ -function getVideoSize(bidRequest, enabledOldFormat = true, enabledFlux = true) { +function getVideoSize(bidRequest, enabledOldFormat = true, enabled1x1 = true) { /** * @param {Array | Array>} sizes - * @return {{w: number, h: number} | null} - @@ -287,10 +321,10 @@ function getVideoSize(bidRequest, enabledOldFormat = true, enabledFlux = true) { playerSize = playerSize || _getPlayerSize(bidRequest.sizes); } - if (enabledFlux) { - // NOTE: `video.playerSize` in Flux is always [1,1]. + if (enabled1x1) { + // NOTE: `video.playerSize` in 1x1 is always [1,1]. if (playerSize && (playerSize.w === 1 && playerSize.h === 1)) { - // NOTE: `params.playerSize` is a specific object to support `FLUX`. + // NOTE: `params.playerSize` is a specific object to support `1x1`. playerSize = _getPlayerSize(deepAccess(bidRequest, 'params.playerSize')); } } @@ -298,6 +332,11 @@ function getVideoSize(bidRequest, enabledOldFormat = true, enabledFlux = true) { return playerSize || DEFAULT_VIDEO_SIZE; } +/** + * Create render for outstream video. + * @param {Object} serverResponse.body - + * @returns {Renderer} - Prebid Renderer object + */ function newRenderer(response) { const renderer = Renderer.install({ id: response.uid, @@ -314,12 +353,21 @@ function newRenderer(response) { return renderer; } +/** + * Handles an outstream response after the library is loaded + * @param {Object} bid + */ function outstreamRender(bid) { bid.renderer.push(() => { window.DACIVTPREBID.renderPrebid(bid); }); } +/** + * Create render for cmer outstream video. + * @param {Object} serverResponse.body - + * @returns {Renderer} - Prebid Renderer object + */ function newCmerRenderer(response) { const renderer = Renderer.install({ id: response.uid, @@ -336,6 +384,10 @@ function newCmerRenderer(response) { return renderer; } +/** + * Handles an outstream response after the library is loaded + * @param {Object} bid + */ function cmerRender(bid) { bid.renderer.push(() => { window.CMERYONEPREBID.renderPrebid(bid); diff --git a/modules/yieldoneBidAdapter.md b/modules/yieldoneBidAdapter.md index 1414d4e464f..d1306a08202 100644 --- a/modules/yieldoneBidAdapter.md +++ b/modules/yieldoneBidAdapter.md @@ -13,8 +13,7 @@ Connect to YIELDONE for bids. THE YIELDONE adapter requires setup and approval from the YIELDONE team. Please reach out to your account team or y1s@platform-one.co.jp for more information. -Note: THE YIELDONE adapter do not support "multi-format" scenario... if both -banner and video are specified as mediatypes, YIELDONE will treat it as a video unit. +YIELDONE adapter supports Banner, Video and Multi-Format(video, banner) currently. # Test Parameters ```javascript @@ -24,13 +23,16 @@ var adUnits = [ code: 'banner-div', mediaTypes: { banner: { - sizes: [[300, 250], [336, 280]] + sizes: [ + [300, 250], + [336, 280] + ] } }, bids: [{ bidder: 'yieldone', params: { - placementId: '36891' + placementId: '36891' // required } }] }, @@ -44,11 +46,109 @@ var adUnits = [ }, }, bids: [{ - bidder: 'yieldone', - params: { - placementId: '41993' - } - }] + bidder: "yieldone", + params: { + placementId: "36892", // required + playerParams: { // optional + wrapperWidth: "320px", // optional + wrapperHeight: "180px" // optional + }, + } + }] + }, + // Video adUnit(mediaTypes.video.playerSize: [1,1]) + { + code: 'video-1x1-div', + mediaTypes: { + video: { + playerSize: [[1, 1]], + context: 'outstream' + }, + }, + bids: [{ + bidder: 'yieldone', + params: { + placementId: "36892", // required + playerSize: [640, 360], // required + playerParams: { // optional + wrapperWidth: "320px", // optional + wrapperHeight: "180px" // optional + }, + } + }] + }, + // Multi-Format adUnit + { + code: "multi-format-div", + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [1, 1] + ] + }, + video: { + playerSize: [640, 360], + context: "outstream" + } + }, + bids: [{ + // * "video" bid object should be placed before "banner" bid object. + // This bid will request a "video" media type ad. + bidder: "yieldone", + params: { + placementId: "36892", // required + playerParams: { // required + wrapperWidth: "320px", // optional + wrapperHeight: "180px" // optional + }, + } + }, + { + // This bid will request a "banner" media type ad. + bidder: "yieldone", + params: { + placementId: "36891" // required + } + } + ] + }, + // Multi-Format adUnit(mediaTypes.video.playerSize: [1,1]) + { + code: "multi-format-1x1-div", + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [1, 1] + ] + }, + video: { + playerSize: [1, 1], + context: "outstream" + } + }, + bids: [{ + // * "video" bid object should be placed before "banner" bid object. + // This bid will request a "video" media type ad. + bidder: "yieldone", + params: { + placementId: "36892", // required + playerSize: [640, 360], // required + playerParams: { // required + wrapperWidth: "320px", // optional + wrapperHeight: "180px" // optional + }, + } + }, + { + // This bid will request a "banner" media type ad. + bidder: "yieldone", + params: { + placementId: "36891" // required + } + } + ] } ``` diff --git a/test/spec/modules/yieldoneBidAdapter_spec.js b/test/spec/modules/yieldoneBidAdapter_spec.js index 59bd4a80081..a10247411db 100644 --- a/test/spec/modules/yieldoneBidAdapter_spec.js +++ b/test/spec/modules/yieldoneBidAdapter_spec.js @@ -140,7 +140,7 @@ describe('yieldoneBidAdapter', function() { }); }); - describe('New Format', function () { + describe('Single Format', function () { const bidRequests = [ { params: {placementId: '0'}, @@ -206,8 +206,11 @@ describe('yieldoneBidAdapter', function() { it('width and height should be set as separate parameters on outstream requests', function () { expect(request[0].data).to.not.have.property('w'); + expect(request[0].data).to.not.have.property('h'); expect(request[1].data).to.not.have.property('w'); + expect(request[1].data).to.not.have.property('h'); expect(request[2].data).to.not.have.property('w'); + expect(request[2].data).to.not.have.property('h'); expect(request[3].data.w).to.equal(1280); expect(request[3].data.h).to.equal(720); expect(request[4].data.w).to.equal(1920); @@ -260,12 +263,13 @@ describe('yieldoneBidAdapter', function() { it('width and height should be set as separate parameters on outstream requests', function () { expect(request[0].data).to.not.have.property('w'); + expect(request[0].data).to.not.have.property('h'); expect(request[1].data.w).to.equal(1920); expect(request[1].data.h).to.equal(1080); }); }); - describe('FLUX Format', function () { + describe('1x1 Format', function () { const bidRequests = [ { // It will be treated as a banner. @@ -326,6 +330,7 @@ describe('yieldoneBidAdapter', function() { it('width and height should be set as separate parameters on outstream requests', function () { expect(request[0].data).to.not.have.property('w'); + expect(request[0].data).to.not.have.property('h'); expect(request[1].data.w).to.equal(1920); expect(request[1].data.h).to.equal(1080); expect(request[2].data.w).to.equal(DEFAULT_VIDEO_SIZE.w); @@ -500,9 +505,9 @@ describe('yieldoneBidAdapter', function() { 'currency': 'JPY', 'statusMessage': 'Bid available', 'dealId': 'P1-FIX-7800-DSP-MON', - 'admoain': [ + 'adomain': [ 'www.example.com' - ] + ], } }; @@ -528,7 +533,16 @@ describe('yieldoneBidAdapter', function() { }]; let result = spec.interpretResponse(serverResponseBanner, bidRequestBanner[0]); expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0].requestId).to.equal(expectedResponse[0].requestId); + expect(result[0].cpm).to.equal(expectedResponse[0].cpm); + expect(result[0].width).to.equal(expectedResponse[0].width); + expect(result[0].height).to.equal(expectedResponse[0].height); + expect(result[0].creativeId).to.equal(expectedResponse[0].creativeId); + expect(result[0].dealId).to.equal(expectedResponse[0].dealId); + expect(result[0].currency).to.equal(expectedResponse[0].currency); expect(result[0].mediaType).to.equal(expectedResponse[0].mediaType); + expect(result[0].ad).to.equal(expectedResponse[0].ad); + expect(result[0].meta.advertiserDomains[0]).to.equal(expectedResponse[0].meta.advertiserDomains[0]); }); let serverResponseVideo = { @@ -537,7 +551,7 @@ describe('yieldoneBidAdapter', function() { 'height': 360, 'width': 640, 'cpm': 0.0536616, - 'dealId': 'P1-FIX-766-DSP-MON', + 'dealId': 'P1-FIX-7800-DSP-MON', 'crid': '2494768', 'currency': 'JPY', 'statusMessage': 'Bid available', @@ -575,7 +589,7 @@ describe('yieldoneBidAdapter', function() { 'currency': 'JPY', 'netRevenue': true, 'ttl': 3000, - 'referrer': '', + 'referrer': 'http%3A%2F%2Flocalhost%3A9876%2F%3Fid%3D74552836', 'meta': { 'advertiserDomains': [] }, @@ -588,9 +602,19 @@ describe('yieldoneBidAdapter', function() { }]; let result = spec.interpretResponse(serverResponseVideo, bidRequestVideo[0]); expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0].requestId).to.equal(expectedResponse[0].requestId); + expect(result[0].cpm).to.equal(expectedResponse[0].cpm); + expect(result[0].width).to.equal(expectedResponse[0].width); + expect(result[0].height).to.equal(expectedResponse[0].height); + expect(result[0].creativeId).to.equal(expectedResponse[0].creativeId); + expect(result[0].dealId).to.equal(expectedResponse[0].dealId); + expect(result[0].currency).to.equal(expectedResponse[0].currency); + expect(result[0].vastXml).to.equal(expectedResponse[0].vastXml); expect(result[0].mediaType).to.equal(expectedResponse[0].mediaType); + expect(result[0].referrer).to.equal(expectedResponse[0].referrer); expect(result[0].renderer.id).to.equal(expectedResponse[0].renderer.id); expect(result[0].renderer.url).to.equal(expectedResponse[0].renderer.url); + expect(result[0].meta.advertiserDomains[0]).to.equal(expectedResponse[0].meta.advertiserDomains[0]); }); it('handles empty bid response', function () { From 4d6c88dcb508b92467b06b0f352a60dd8dfb3e9e Mon Sep 17 00:00:00 2001 From: John Conway Date: Tue, 15 Nov 2022 04:52:25 -0500 Subject: [PATCH 436/569] Zeus Prime RTD Module: initial module release (#9165) * docs(zeusPrime): added zeusPrimeRtdProvider documenation * test: added zeusPrimeRtdProvider tests * feat: added zeusPrime rtd submodule * chore: fix issue with params in the initModule function * chore: lint fixes * chore: lint fixes for tests * test: mock subtle since some tests run insecure crypto.subtle is only available in secure instances on new browsers, and browserstack runs some tests not in secure mode, so crypto.subtle is undefine. This just creates a mock sublte to avoid this in the tests. * test: try to override subtle when it doesnt exist in tests * test: remove subtle restore * chore: added ZeusPrime to .submodules.json --- modules/.submodules.json | 3 +- modules/zeusPrimeRtdProvider.js | 357 +++++++++++++++ modules/zeusPrimeRtdProvider.md | 60 +++ .../spec/modules/zeusPrimeRtdProvider_spec.js | 410 ++++++++++++++++++ 4 files changed, 829 insertions(+), 1 deletion(-) create mode 100644 modules/zeusPrimeRtdProvider.js create mode 100644 modules/zeusPrimeRtdProvider.md create mode 100644 test/spec/modules/zeusPrimeRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index aad778be67b..52bbc638c79 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -71,7 +71,8 @@ "reconciliationRtdProvider", "sirdataRtdProvider", "timeoutRtdProvider", - "weboramaRtdProvider" + "weboramaRtdProvider", + "zeusPrimeRtdProvider" ], "fpdModule": [ "enrichmentFpdModule", diff --git a/modules/zeusPrimeRtdProvider.js b/modules/zeusPrimeRtdProvider.js new file mode 100644 index 00000000000..28c46957c50 --- /dev/null +++ b/modules/zeusPrimeRtdProvider.js @@ -0,0 +1,357 @@ +/** + * This module adds Zeus Insights For Publishers (ZIP) provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * + * This module will request the article topics for the current page and add them as page keyvalues + * for the ad requests. + * + * @module modules/zeusInsightsForPublishersRtdProvider + * @requires module:modules/realTimeData + */ + +import { logInfo, logError, logWarn, logMessage } from '../src/utils.js' +import { submodule } from '../src/hook.js' +import { ajaxBuilder } from '../src/ajax.js' + +class Logger { + get showDebug() { + if (this._showDebug === true || this._showDebug === false) { + return this._showDebug + } + + return window.zeusPrime?.debug || false + } + set showDebug(shouldShow) { + this._showDebug = shouldShow + } + + get error() { + return logError.bind(this, 'zeusPrimeRtdProvider: ') + } + get warn() { + return logWarn.bind(this, 'zeusPrimeRtdProvider: ') + } + get info() { + return logInfo.bind(this, 'zeusPrimeRtdProvider: ') + } + get debug() { + if (this.showDebug) { + return logMessage.bind(this, 'zeusPrimeRtdProvider: ') + } + + return () => {} + } +} + +var logger = new Logger() + +function loadCommandQueue() { + window.zeusPrime = window.zeusPrime || { cmd: [] } + const queue = [...window.zeusPrime.cmd] + + window.zeusPrime.cmd = [] + window.zeusPrime.cmd.push = (callback) => { + callback(window.zeusPrime) + } + + queue.forEach((callback) => callback(window.zeusPrime)) +} + +function markStatusComplete(key) { + const status = window?.zeusPrime?.status + if (status) { + status[key] = true + } +} + +function createStatus() { + if (window.zeusPrime && !window.zeusPrime.status) { + Object.defineProperty(window.zeusPrime, 'status', { + enumerable: false, + value: { + initComplete: false, + primeKeyValueSet: false, + insightsReqSent: false, + insightsReqReceived: false, + insightsKeyValueSet: false, + scriptComplete: false, + }, + }) + } +} + +function loadPrimeQueryParams() { + try { + const params = new URLSearchParams(window.location.search) + params.forEach((paramValue, paramKey) => { + if (!paramKey.startsWith('zeus_prime_')) { + return + } + + let key = paramKey.replace('zeus_prime_', '') + let value = paramValue.toLowerCase() + + if (value === 'true' || value === '1') { + value = true + } else if (value === 'false' || value === '0') { + value = false + } + + window.zeusPrime[key] = value + }) + } catch (_) {} +} + +const DEFAULT_API = 'https://insights.zeustechnology.com' + +function init(gamId = null, options = {}) { + window.zeusPrime = window.zeusPrime || { cmd: [] } + + window.zeusPrime.gamId = gamId || options.gamId || window.zeusPrime.gamId || undefined + window.zeusPrime.api = DEFAULT_API + window.zeusPrime.hostname = options.hostname || window.location?.hostname || '' + window.zeusPrime.pathname = options.pathname || window.location?.pathname || '' + window.zeusPrime.pageUrl = `${window.zeusPrime.hostname}${window.zeusPrime.pathname}` + window.zeusPrime.pageHash = options.pageHash || null + window.zeusPrime.debug = window.zeusPrime.debug || options.debug === true || false + window.zeusPrime.disabled = window.zeusPrime.disabled || options.disabled === true || false + + loadPrimeQueryParams() + + logger.showDebug = window.zeusPrime.debug + + createStatus() + markStatusComplete('initComplete') +} + +function setTargeting() { + const { gamId, hostname } = window.zeusPrime + + if (typeof gamId !== 'string') { + throw new Error(`window.zeusPrime.gamId must be a string. Received: ${String(gamId)}`) + } + + addKeyValueToGoogletag(`zeus_${gamId}`, hostname) + logger.debug(`Setting zeus_${gamId}=${hostname}`) + markStatusComplete('primeKeyValueSet') +} + +function setPrimeAsDisabled() { + addKeyValueToGoogletag('zeus_prime', 'false') + logger.debug('Disabling prime; Setting key-value zeus_prime to false') +} + +function addKeyValueToGoogletag(key, value) { + window.googletag = window.googletag || { cmd: [] } + window.googletag.cmd.push(function () { + window.googletag.pubads().setTargeting(key, value) + }) +} + +function isInsightsPage(pathname = '') { + const NOT_SECTIONS = [ + { + test: /\/search/, + type: 'search', + }, + { + test: /\/author/, + type: 'author', + }, + { + test: /\/event/, + type: 'event', + }, + { + test: /\/homepage/, + type: 'front', + }, + { + test: /^\/?$/, + type: 'front', + }, + ] + + const typeObj = NOT_SECTIONS.find((pg) => pathname.match(pg.test)) + return typeObj === undefined +} + +async function getUrlHash(canonical) { + try { + const buf = await window.crypto.subtle.digest( + 'SHA-1', + new TextEncoder('utf-8').encode(canonical) + ) + const hashed = Array.prototype.map + .call(new Uint8Array(buf), (x) => `00${x.toString(16)}`.slice(-2)) + .join('') + + return hashed + } catch (e) { + logger.error('Failed to load hash', e.message) + logger.debug('Exception', e) + return '' + } +} + +async function sendPrebidRequest(url) { + return new Promise((resolve, reject) => { + const ajax = ajaxBuilder() + ajax(url, { + success: (responseText, response) => { + resolve({ + ...response, + status: response.status, + json: () => JSON.parse(responseText), + }) + }, + + error: (responseText, response) => { + if (!response.status) { + reject(response) + } + + let json = responseText + if (responseText) { + try { + json = JSON.parse(responseText) + } catch (_) { + json = null + } + } + + resolve({ + status: response.status, + json: () => json || null, + responseValue: json, + }) + }, + }) + }) +} + +async function requestTopics() { + const { api, hostname, pageUrl } = window.zeusPrime + + if (!window.zeusPrime.pageHash) { + window.zeusPrime.pageHash = await getUrlHash(pageUrl) + } + + const pageHash = window.zeusPrime.pageHash + const zeusInsightsUrl = `${api}/${hostname}/${pageHash}?article_location=${pageUrl}` + + logger.debug('Requesting topics', zeusInsightsUrl) + try { + markStatusComplete('insightsReqSent') + const response = await sendPrebidRequest(zeusInsightsUrl) + if (response.status === 200) { + logger.debug('topics found') + markStatusComplete('insightsReqReceived') + return await response.json() + } else if ( + response.status === 204 || + response.status < 200 || + (response.status >= 300 && response.status <= 399) + ) { + logger.debug('no topics found') + markStatusComplete('insightsReqReceived') + return null + } else { + logger.error(`Topics request returned error: ${response.status}`) + markStatusComplete('insightsReqReceived') + return null + } + } catch (e) { + logger.error('failed to request topics', e) + return null + } +} + +function setTopicsTargeting(topics = []) { + if (topics.length === 0) { + return + } + + window.googletag = window.googletag || { cmd: [] } + window.googletag.cmd.push(function () { + window.googletag.pubads().setTargeting('zeus_insights', topics) + }) + + markStatusComplete('insightsKeyValueSet') +} + +async function startTopicsRequest() { + if (isInsightsPage(window.zeusPrime.pathname)) { + const response = await requestTopics() + if (response) { + setTopicsTargeting(response?.topics) + } + } else { + logger.debug('This page is not eligible for topics, request will be skipped') + } +} + +async function run(gamId, options = {}) { + logger.showDebug = options.debug || false + + try { + init(gamId, options) + loadCommandQueue() + + if (window.zeusPrime.disabled) { + setPrimeAsDisabled() + } else { + setTargeting() + await startTopicsRequest() + } + } catch (e) { + logger.error('Failed to run.', e.message || e) + } finally { + markStatusComplete('scriptComplete') + } +} + +/** + * @preserve + * Initializes the ZeusPrime RTD Submodule. The config provides the GamID for this + * site that is used to configure Prime. + * @param {object} config The Prebid configuration for this module. + * @param {object} config.params The parameters for this module. + * @param {string} config.params.gamId The Gam ID (or Network Code) in GAM for this site. + */ +function initModule(config) { + const { params } = config || {} + const { gamId, ...rest } = params || {} + run(gamId, rest) +} + +/** + * @preserve + * @type {RtdSubmodule} + */ +const zeusPrimeSubmodule = { + /** + * @preserve + * The name of the plugin. + * @type {string} + */ + name: 'zeusPrime', + + /** + * @preserve + * ZeusPrime use + */ + init: initModule, +} + +/** + * @preserve + * Register the Sub Module. + */ +function registerSubModule() { + submodule('realTimeData', zeusPrimeSubmodule) +} + +registerSubModule() + +export { zeusPrimeSubmodule } diff --git a/modules/zeusPrimeRtdProvider.md b/modules/zeusPrimeRtdProvider.md new file mode 100644 index 00000000000..f3a6c5018d5 --- /dev/null +++ b/modules/zeusPrimeRtdProvider.md @@ -0,0 +1,60 @@ +# Overview + +Module Name: Zeus Prime RTD Provider +Module Type: Rtd Provider +Maintainer: support@zeustechnology.com + +## Description + +The Zeus Prime RTD Provider provides integration of Zeus Prime onto sites with Prebid. This module will request information from Zeus Prime servers to add the page level targeting required for Prime into the customer's ad setup. + +Zeus Prime runs as soon as the code is initialized, so it can retrieve the information required from the Zeus Prime server to create the targeting key-values. Zeus Prime will provide two page level key-values: `zeus_` and `zeus_insights`. Zeus Prime provides contextual information about a pages content, and does not provide user information that could present privacy implications. + +For more information and help with setting up Zeus Prime, see the [onboarding documentation site](https://onboarding.zeustechnology.com). + +## Usage + +To use Zeus Prime, add `zeusPrimeRtdProvider` into your Prebid build: + +``` +gulp build --modules=rtdModule,zeusPrimeRtdProvider +``` + +> Note that the global RTD module, `rtdModule`, is required for the Zeus Prime RTD module. + +Once the code is included, configure Zeus Prime in your Prebid configuration: + +```javascript +pbjs.setConfig({ + ..., + realTimeData: { + dataProviders: [{ + name: 'zeusPrime', + waitForIt: false, + params: { + gamId: '' + } + }] + }, + ... +}) +``` + +## Parameters + +The parameters below describe the configuration object used to configure Zeus Prime. + +| Name | Type | Description | Default | +|------------------------|----------|-----------------------------------------------------------------------------------------------|---------| +| name | String | This will always be `zeusPrime` | - | +| waitForIt | Boolean | Should be false since Zeus Prime runs on initial load and not during the bidding cycle. | `false` | +| params | Object | | - | +| params.gamId | String | The gamId or Google Ad Manager Network Code to access your Google Ad Manager instance. | - | + +### `gamId` Parameter + +Zeus Prime requires the gamId parameter, or the Google Ad Manager Network Code, to reference your account. See the [Google documentation](https://support.google.com/admanager/answer/7674889?hl=en) to find out where you can retrieve the Network Code. + +## Troubleshooting + +For troubleshooting steps and guides to assist with verifying your Zeus Prime installation, see our [installation documentation](https://onboarding.zeustechnology.com/docs/installation). \ No newline at end of file diff --git a/test/spec/modules/zeusPrimeRtdProvider_spec.js b/test/spec/modules/zeusPrimeRtdProvider_spec.js new file mode 100644 index 00000000000..294d98df377 --- /dev/null +++ b/test/spec/modules/zeusPrimeRtdProvider_spec.js @@ -0,0 +1,410 @@ +import { zeusPrimeSubmodule } from 'modules/zeusPrimeRtdProvider'; +import { server } from 'test/mocks/xhr.js'; +import * as utils from 'src/utils'; + +async function waitForStatus(statusVar) { + const MAX_COUNT = 20; + let count = 0; + while ( + count <= MAX_COUNT && + window.zeusPrime.status && + window.zeusPrime.status[statusVar] !== true + ) { + count += 1; + await new Promise((resolve) => setTimeout(resolve, 10)); + } + + if (count === MAX_COUNT) { + throw new Error('Timeout waiting for zeusPrimeRtdProvider to complete'); + } +} + +/** + * Execute all the commands in the googletag.cmd queue. + */ +function executeGoogletagTargeting() { + window.googletag.cmd.forEach((cmd) => cmd()); +} + +describe('Zeus Prime RTD submodule', () => { + let logErrorSpy; + let logMessageSpy; + let setTargetingStub; + let originalGtag; + + beforeEach(() => { + logErrorSpy = sinon.spy(utils, 'logError'); + logMessageSpy = sinon.spy(utils, 'logMessage'); + setTargetingStub = sinon.stub(); + window.zeusPrime = { cmd: [] }; + originalGtag = window.googletag; + window.googletag = { + cmd: [], + pubads: () => ({ + setTargeting: setTargetingStub, + }), + }; + + // Mock subtle since this doesnt exists in some test environments due to security in newer browsers. + if (typeof window.crypto.subtle === 'undefined') { + Object.defineProperty(crypto, 'subtle', { value: { digest: () => 'mockHash' } }) + } + }); + + afterEach(() => { + logErrorSpy.restore(); + logMessageSpy.restore(); + window.googletag = originalGtag; + }); + + it('should init and set key-value for zeus_', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/', + }, + }); + + // wait for the script to finish + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(window.googletag.cmd).to.have.length(1); + sinon.assert.callCount(setTargetingStub, 1); + sinon.assert.calledWith(setTargetingStub, 'zeus_1234', 'www.example.com'); + }); + + it('should init and set key-value for zeus_ from command queue', async () => { + window.zeusPrime.cmd.push((prime) => (prime.gamId = '9876')); + zeusPrimeSubmodule.init({ + params: { + hostname: 'www.example.com', + pathname: '/', + }, + }); + + // wait for the script to finish + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(window.googletag.cmd).to.have.length(1); + sinon.assert.callCount(setTargetingStub, 1); + sinon.assert.calledWith(setTargetingStub, 'zeus_9876', 'www.example.com'); + }); + + it('should init with values from location and set key-value for zeus_', async () => { + zeusPrimeSubmodule.init({ params: { gamId: '1234' } }); + + // wait for the script to finish + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(window.googletag.cmd).to.have.length(1); + sinon.assert.callCount(setTargetingStub, 1); + sinon.assert.calledWith(setTargetingStub, 'zeus_1234', 'localhost'); + expect(window.zeusPrime.pathname).to.equal('/context.html'); + }); + + it('should emit error when gamId is not set', async () => { + zeusPrimeSubmodule.init({}); + + // wait for the script to finish + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(window.googletag.cmd).to.have.length(0); + sinon.assert.callCount(setTargetingStub, 0); + sinon.assert.calledWith( + logErrorSpy, + 'zeusPrimeRtdProvider: ', + 'Failed to run.', + 'window.zeusPrime.gamId must be a string. Received: undefined' + ); + }); + + it('should not make a call to the server when url is a homepage', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/', + }, + }); + + // wait for the script to finish + await waitForStatus('scriptComplete'); + + expect(server.requests).to.have.length(0); + }); + + it('should make a call to the server and set key-vlaue when url is an article page and returns topics', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('insightsReqReceived'); + + // Response + server.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + '{"topics": ["bs0"]}' + ); + + // Wait for the script to process the response + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(server.requestCount).to.be.equal(1); + expect(window.googletag.cmd).to.have.length(2); + sinon.assert.calledTwice(setTargetingStub); + sinon.assert.calledWith( + setTargetingStub.firstCall, + 'zeus_1234', + 'www.example.com' + ); + sinon.assert.calledWith(setTargetingStub.secondCall, 'zeus_insights', [ + 'bs0', + ]); + sinon.assert.notCalled(logErrorSpy); + }); + + it('should not set insights keyvalue when server returns 204', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('insightsReqReceived'); + + // Response + server.requests[0].respond(204, { 'Content-Type': 'application/json' }); + + // Wait for the script to process the response + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(server.requestCount).to.be.equal(1); + expect(window.googletag.cmd).to.have.length(1); + sinon.assert.calledOnce(setTargetingStub); + sinon.assert.calledWith( + setTargetingStub.firstCall, + 'zeus_1234', + 'www.example.com' + ); + sinon.assert.notCalled(logErrorSpy); + }); + + it('should not set insights keyvalue when server returns empty topics array', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('insightsReqReceived'); + + // Respond + server.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + '{ "topics": [] }' + ); + + // Wait for the script to process the response + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(server.requestCount).to.be.equal(1); + expect(window.googletag.cmd).to.have.length(1); + sinon.assert.calledOnce(setTargetingStub); + sinon.assert.calledWith( + setTargetingStub.firstCall, + 'zeus_1234', + 'www.example.com' + ); + sinon.assert.notCalled(logErrorSpy); + }); + + it('should not set insights keyvalue and emit error when server returns error status (400)', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('insightsReqReceived'); + + // Response + server.requests[0].respond( + 404, + { 'Content-Type': 'application/json' }, + '{"message": "Not found"}' + ); + + // Wait for the script to process the response + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(server.requestCount).to.be.equal(1); + expect(window.googletag.cmd).to.have.length(1); + sinon.assert.calledOnce(setTargetingStub); + sinon.assert.calledWith( + setTargetingStub.firstCall, + 'zeus_1234', + 'www.example.com' + ); + sinon.assert.calledWith( + logErrorSpy, + 'zeusPrimeRtdProvider: ', + 'Topics request returned error: 404' + ); + }); + + it('should not set insights keyvalue and emit error when response is not received (network request)', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('insightsReqReceived'); + + // Response + server.requests[0].error(); + + // Wait for the script to process the response + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(server.requestCount).to.be.equal(1); + expect(window.googletag.cmd).to.have.length(1); + sinon.assert.calledOnce(setTargetingStub); + sinon.assert.calledWith( + setTargetingStub.firstCall, + 'zeus_1234', + 'www.example.com' + ); + sinon.assert.calledWith( + logErrorSpy, + 'zeusPrimeRtdProvider: ', + 'failed to request topics' + ); + }); + + it('fails gracefully when crypto fails', async () => { + const digestStub = sinon.stub(window.crypto.subtle, 'digest'); + digestStub.throwsException('Failed to generate digest.'); + + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('scriptComplete'); + + sinon.assert.calledWith( + logErrorSpy, + 'zeusPrimeRtdProvider: ', + 'Failed to load hash' + ); + + digestStub.restore(); + }); + + it('script should add zeus_prime key and not send request when disabled is set', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + disabled: true, + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('scriptComplete'); + + // execute any googletag commands added to the queue during execution + executeGoogletagTargeting(); + + expect(server.requestCount).to.be.equal(0); + expect(window.googletag.cmd).to.have.length(1); + sinon.assert.calledWith(setTargetingStub, 'zeus_prime', 'false'); + }); + + it('debug true enables debug logging', async () => { + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + debug: true, + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('scriptComplete'); + + sinon.assert.called(logMessageSpy); + sinon.assert.notCalled(logErrorSpy); + }); + + it('debug false disables debug logging', async () => { + window.zeusPrime.disabled = false; + zeusPrimeSubmodule.init({ + params: { + gamId: '1234', + hostname: 'www.example.com', + pathname: '/article/some-article', + }, + }); + + // Wait for request to be sent + await waitForStatus('scriptComplete'); + + sinon.assert.notCalled(logMessageSpy); + sinon.assert.notCalled(logErrorSpy); + }); +}); From 0813039d91c91639903d5b7a92991fff2082cf79 Mon Sep 17 00:00:00 2001 From: Oleksandr Soldatov Date: Tue, 15 Nov 2022 11:53:01 +0200 Subject: [PATCH 437/569] Captify RTD Submodule: Initial release (#9180) * Captify Live-classification Rtd Submodule * Fix code-review comments --- .../gpt/captifyRtdProvider_example.html | 167 ++++++++++++ modules/.submodules.json | 1 + modules/captifyRtdProvider.js | 146 ++++++++++ modules/captifyRtdProvider.md | 68 +++++ test/spec/modules/captifyRtdProvider_spec.js | 253 ++++++++++++++++++ 5 files changed, 635 insertions(+) create mode 100644 integrationExamples/gpt/captifyRtdProvider_example.html create mode 100644 modules/captifyRtdProvider.js create mode 100644 modules/captifyRtdProvider.md create mode 100644 test/spec/modules/captifyRtdProvider_spec.js diff --git a/integrationExamples/gpt/captifyRtdProvider_example.html b/integrationExamples/gpt/captifyRtdProvider_example.html new file mode 100644 index 00000000000..955fbf8be70 --- /dev/null +++ b/integrationExamples/gpt/captifyRtdProvider_example.html @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + +
+

+ Module will add key/value pairs in ad calls. + Check out for captify_segments key in the payload sent to Xandr to endpoint https://ib.adnxs.com/ut/v3/prebid : keywords.key[captify_segments] should have an array of string as value. + This array will have Xandr RTSS segment ids. +

+
+

Basic Prebid.js Example with CaptifyRTD

+
Div-1
+
+ +
+ + + diff --git a/modules/.submodules.json b/modules/.submodules.json index 52bbc638c79..f8c30459e1e 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -57,6 +57,7 @@ "akamaiDapRtdProvider", "blueconicRtdProvider", "browsiRtdProvider", + "captifyRtdProvider", "dgkeywordRtdProvider", "geoedgeRtdProvider", "hadronRtdProvider", diff --git a/modules/captifyRtdProvider.js b/modules/captifyRtdProvider.js new file mode 100644 index 00000000000..b97aa49a48e --- /dev/null +++ b/modules/captifyRtdProvider.js @@ -0,0 +1,146 @@ +/** + * This module adds Captify real time data provider module + * The {@link module:modules/realTimeData} module is required + * The module will fetch segments (page-centric) from Captify live-classification server + * @module modules/captifyRtdProvider + * @requires module:modules/realTimeData + */ +import { submodule } from '../src/hook.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import {deepAccess, isArray, isEmptyStr, isNumber, logError} from '../src/utils.js'; +import {getGlobal} from '../src/prebidGlobal.js'; + +const MODULE_NAME = 'realTimeData'; +const SUBMODULE_NAME = 'CaptifyRTDModule'; +const DEFAULT_LC_URL = 'https://live-classification.cpx.to/prebid-segments'; + +const STATUS = { + SUCCESS: 200, + ACCEPTED: 202, +}; + +/** + * Set `appnexusAuctionKeywords` that appnexus bidder will read and send in request to Xandr + * @param {Array} segments captify segments for Appnexus(Xandr) system, in form of [id1, id2, id3] + * where id1, id2, id3 - actual Xandr segment ids with keywords enabled + */ +export function setAppnexusSegments(segments) { + config.setConfig({ + appnexusAuctionKeywords: { + 'captify_segments': segments, + }, + }) +} + +/** + * Function returns only bidders that contained both, in moduleConfig and at least one adUnit. + * @param {Array} bidders Contains list of bidders, to set targeting for + * @param {Object} data Response of live-classification service + */ +export function addSegmentData(bidders, data) { + for (const bidder of bidders) { + if (bidder === 'appnexus') { setAppnexusSegments(data['xandr']) } + } +} + +/** + * Function returns only bidders that contained in both, moduleConfig and at least one adUnit. + * @param {Object} moduleConfig Config object passed to the module + * @param {Object} reqBidsConfigObj Config object for the bidders; each adapter has its own entry + */ +export function getMatchingBidders(moduleConfig, reqBidsConfigObj) { + const biddersFromConf = deepAccess(moduleConfig, 'params.bidders'); + + const adUnitBidders = reqBidsConfigObj.adUnits + .flatMap(({bids}) => bids.map(({bidder}) => bidder)) + .filter((e, i, a) => a.indexOf(e) === i); + + if (!isArray(adUnitBidders) || !adUnitBidders.length) { + logError(SUBMODULE_NAME, 'Missing parameter bidders in bidRequestConfig'); + return []; + } + + return biddersFromConf.filter(bidder => adUnitBidders.includes(bidder)); +} + +/** + * Main function that sets Captify targeting for various bidders + * @param {Object} moduleConfig Config object passed to the module + * @param {Function} onDone callback function that executed when everything is done + * @param {Object} reqBidsConfigObj Config object for the bidders; each adapter has its own entry + * @param {Object} gcv contains data related to user consent, if applies + */ +export function setCaptifyTargeting(reqBidsConfigObj, onDone, moduleConfig, gcv) { + const pbjsVer = getGlobal(); + const ref = getRefererInfo().referer; + const url = document.URL; + const pubId = moduleConfig.params.pubId; + const bidders = getMatchingBidders(moduleConfig, reqBidsConfigObj) + const requestBody = { + pubId, + ref, + url, + pbjsVer, + gcv + }; + let requestUrl = moduleConfig.params.url; + if (!requestUrl || isEmptyStr(requestUrl)) { + requestUrl = DEFAULT_LC_URL; + } + + if (!bidders.length) { + logError(SUBMODULE_NAME, 'There are no matched bidders to work with'); + return; + } + + ajax(requestUrl, { + success: function (response, req) { + if (req.status === STATUS.SUCCESS) { + try { + const data = JSON.parse(response); + if (data) { + addSegmentData(bidders, data); + } + } catch (e) { + logError(SUBMODULE_NAME, 'Unable to parse live-classification data' + e); + } + } + onDone(); + }, + error: function () { + onDone(); + logError(SUBMODULE_NAME, 'Unable to get live-classification data'); + } + }, + JSON.stringify(requestBody), + { + contentType: 'application/json', + method: 'POST', + }); +} + +export function init(moduleConfig, userConsent) { + // Validate bidders + const biddersFromConf = deepAccess(moduleConfig, 'params.bidders'); + if (!isArray(biddersFromConf) || !biddersFromConf.length) { + logError(SUBMODULE_NAME, 'Missing parameter bidders in moduleConfig'); + return false + } + const publisherId = deepAccess(moduleConfig, 'params.pubId'); + // Publisher Id + if (!isNumber(publisherId)) { + logError(SUBMODULE_NAME, 'Missing parameter pubId in moduleConfig'); + return false + } + return true +} + +export const captifySubmodule = { + name: SUBMODULE_NAME, + init: init, + setCaptifyTargeting +}; + +submodule(MODULE_NAME, captifySubmodule); diff --git a/modules/captifyRtdProvider.md b/modules/captifyRtdProvider.md new file mode 100644 index 00000000000..a1a8e9c273f --- /dev/null +++ b/modules/captifyRtdProvider.md @@ -0,0 +1,68 @@ +# Captify Real-Time Data Submodule + +# Overview + + Module Name: Captify Rtd Provider + Module Type: Rtd Provider + Layout: integrationExamples/gpt/captifyRtdProvider_example.html + Maintainer: prebid@captify.tech + +# Description + +Captify uses publisher first-party on-site search data to power machine learning algorithms to create a suite of +contextual based targeting solutions that activate in a cookieless environment. + +The RTD submodule allows bid requests to be classified by our live-classification service on the first ad call, +maximising value for publishers by increasing scale for advertisers. + +Segments will be attached to bid request objects sent to different SSPs in order to optimize targeting. + +Contact prebid@captify.tech for information. + +### Publisher Usage + +Compile the Captify RTD module into your Prebid build: + +`npm ci && gulp build --modules=rtdModule,appnexusBidAdapter,captifyRtdProvider` + +Add the Captify RTD provider to your Prebid config. + +```javascript +pbjs.setConfig({ + realTimeData: { + auctionDelay: 1000, + dataProviders: [ + { + name: "CaptifyRTDModule", + waitForIt: true, + params: { + pubId: 123456, + bidders: ['appnexus'], + } + } + ] + } +}); +``` + +### Parameter Description +This module is configured as part of the `realTimeData.dataProviders` object. + +| Name |Type | Description |Mandatory | Notes | +| :------------- | :------------ | :------------------------------------------------------------------ |:---------|:------------ | +| name | String | Real time data module name | yes | Always 'CaptifyRTDModule' | +| waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (recommended) | no | Default `false` | +| params | Object | | | | +| params.pubId | Integer | Partner ID, required to get results and provided by Captify | yes | Use 123456 for tests and speak to your Captify account manager to receive your pubId | +| params.bidders | Array | List of bidders for which you would like data to be set | yes | Currently only 'appnexus' supported | +| params.url | String | Captify live-classification service url | no | Defaults to `https://live-classification.cpx.to/prebid-segments` + +### Testing + +To view an example of available segments returned by Captify: + +`gulp serve --modules=rtdModule,captifyRtdProvider,appnexusBidAdapter` + +and then point your browser at: + +`http://localhost:9999/integrationExamples/gpt/captifyRtdProvider_example.html?pbjs_debug=true` diff --git a/test/spec/modules/captifyRtdProvider_spec.js b/test/spec/modules/captifyRtdProvider_spec.js new file mode 100644 index 00000000000..2e1052e000f --- /dev/null +++ b/test/spec/modules/captifyRtdProvider_spec.js @@ -0,0 +1,253 @@ +import {addSegmentData, captifySubmodule, getMatchingBidders, setCaptifyTargeting} from 'modules/captifyRtdProvider.js'; +import {server} from 'test/mocks/xhr.js'; +import {config} from 'src/config.js'; +import {deepAccess} from '../../../src/utils'; + +const responseHeader = {'Content-Type': 'application/json'}; +const defaultRequestUrl = 'https://live-classification.cpx.to/prebid-segments'; + +describe('captifyRtdProvider', function () { + describe('init function', function () { + it('successfully instantiates, when configured properly', function () { + const config = { + params: { + pubId: 123456, + bidders: ['appnexus'], + } + }; + expect(captifySubmodule.init(config, null)).to.equal(true); + }); + + it('return false on init, when config is invalid', function () { + const config = { + params: {} + }; + expect(captifySubmodule.init(config, null)).to.equal(false); + expect(captifySubmodule.init(null, null)).to.equal(false); + }); + + it('return false on init, when pubId is absent', function () { + const config = { + params: { + bidders: ['appnexus'], + } + }; + expect(captifySubmodule.init(config, null)).to.equal(false); + expect(captifySubmodule.init(null, null)).to.equal(false); + }); + + it('return false on init, when bidders is empty array', function () { + const config = { + params: { + bidders: [], + pubId: 123, + } + }; + expect(captifySubmodule.init(config, null)).to.equal(false); + expect(captifySubmodule.init(null, null)).to.equal(false); + }); + }); + + describe('addSegmentData function', function () { + it('adds segment data', function () { + config.resetConfig(); + + let data = { + xandr: [111111, 222222], + }; + + addSegmentData(['appnexus'], data); + expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.eql(data['xandr']); + }); + }); + + describe('getMatchingBidders function', function () { + it('returns only bidders that used within adUnits', function () { + const moduleConfig = { + params: { + pubId: 123, + bidders: ['appnexus', 'pubmatic'], + } + }; + let reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }] + }] + }; + + let matchedBidders = getMatchingBidders(moduleConfig, reqBidsConfigObj); + expect(matchedBidders).to.eql(['appnexus']); + }); + + it('return empty result, when there are no bidders configured for adUnits', function () { + const moduleConfig = { + params: { + pubId: 123, + bidders: ['pubmatic'], + } + }; + let reqBidsConfigObj = { + adUnits: [{ + bids: [] + }] + }; + expect(getMatchingBidders(moduleConfig, reqBidsConfigObj)).to.be.empty; + }); + + it('return empty result, when there are no bidders matched', function () { + const moduleConfig = { + params: { + pubId: 123, + bidders: ['pubmatic'], + } + }; + let reqBidsConfigObj = { + adUnits: [{ + bids: [{ + params: { + bidder: 'appnexus', + placementId: 13144370, + } + }] + }] + }; + expect(getMatchingBidders(moduleConfig, reqBidsConfigObj)).to.be.empty; + }); + + it('return empty result, when there are no adUnits with bidders', function () { + const moduleConfig = { + params: { + pubId: 123, + bidders: ['pubmatic'], + } + }; + let reqBidsConfigObj = { + adUnits: [{ + bids: [{ + params: { + placementId: 13144370, + } + }] + }] + }; + expect(getMatchingBidders(moduleConfig, reqBidsConfigObj)).to.be.empty; + }); + }); + + describe('integration test with mock live-classification response', function () { + const moduleConfig = { + params: { + pubId: 123456, + bidders: ['appnexus'], + } + }; + + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }, { + bidder: 'other' + }] + }] + }; + + const expectedUrlParam = 'http://localhost:9876/context.html'; + + it('gets data from async request and adds segment data', function () { + config.resetConfig(); + let data = {xandr: [111111, 222222]}; + const callbackSpy = sinon.spy(); + setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); + let request = server.requests[0]; + let requestBody = JSON.parse(server.requests[0].requestBody); + expect(request.url).to.be.eq(defaultRequestUrl); + expect(requestBody['pubId']).to.eq(moduleConfig.params.pubId); + expect(requestBody['url']).to.eq(expectedUrlParam); + request.respond(200, responseHeader, JSON.stringify(data)); + expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.eql(data['xandr']); + expect(callbackSpy.calledOnce).to.be.true; + }); + + it('do not send classification request, if no matching adUnits on page', function () { + config.resetConfig(); + let reqBidsConfigObj = { + adUnits: [{ + bids: [ + {bidder: 'pubmatic'} + ] + }] + }; + const callbackSpy = sinon.spy(); + setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); + expect(server.requests).to.be.empty; + }); + + it('gets data from async request and adds segment data, using URL from config', function () { + config.resetConfig(); + let data = {xandr: [111111, 222222]}; + const callbackSpy = sinon.spy(); + const testUrl = 'http://my-test-server.com/path'; + const conf = { + params: { + url: testUrl, + pubId: 123456, + bidders: ['appnexus'], + } + }; + setCaptifyTargeting(reqBidsConfigObj, callbackSpy, conf, {}); + let request = server.requests[0]; + let requestBody = JSON.parse(server.requests[0].requestBody); + expect(request.url).to.be.eq(testUrl); + expect(requestBody['pubId']).to.eq(conf.params.pubId); + expect(requestBody['url']).to.eq(expectedUrlParam); + request.respond(200, responseHeader, JSON.stringify(data)); + expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.eql(data['xandr']); + expect(callbackSpy.calledOnce).to.be.true; + }); + + it('do not set anything, in case server responded with 202', function () { + config.resetConfig(); + const callbackSpy = sinon.spy(); + setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); + let request = server.requests[0]; + let requestBody = JSON.parse(server.requests[0].requestBody); + expect(request.url).to.be.eq(defaultRequestUrl); + expect(requestBody['pubId']).to.eq(moduleConfig.params.pubId); + expect(requestBody['url']).to.eq(expectedUrlParam); + request.respond(202, responseHeader, ''); + expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.be.undefined; + expect(callbackSpy.calledOnce).to.be.true; + }); + + it('do not set anything, in case server responded with error', function () { + config.resetConfig(); + const callbackSpy = sinon.spy(); + setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); + let request = server.requests[0]; + expect(request.url).to.be.eq(defaultRequestUrl); + request.respond(500, null, ''); + expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.be.undefined; + expect(callbackSpy.calledOnce).to.be.true; + }); + + it('do not set anything, in case request error', function () { + config.resetConfig(); + const callbackSpy = sinon.spy(); + setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); + let request = server.requests[0]; + expect(request.url).to.be.eq(defaultRequestUrl); + request.abort('test error'); + expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.be.undefined; + expect(callbackSpy.calledOnce).to.be.true; + }); + }); +}); From e7b2e86412d3f4b709954aa10457557f0c37c743 Mon Sep 17 00:00:00 2001 From: Mikhail Ivanchenko Date: Tue, 15 Nov 2022 15:18:38 +0300 Subject: [PATCH 438/569] nextMillennium Bid Adapter: a new cookiesync URL (#9221) * add video support * Sync URL was changed --- modules/nextMillenniumBidAdapter.js | 42 +++++++++---------- .../modules/nextMillenniumBidAdapter_spec.js | 7 ++-- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 762c571f1e6..802a8ac25b0 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -22,7 +22,7 @@ import { getRefererInfo } from '../src/refererDetection.js'; const BIDDER_CODE = 'nextMillennium'; const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; const TEST_ENDPOINT = 'https://test.pbs.nextmillmedia.com/openrtb2/auction'; -const SYNC_ENDPOINT = 'https://statics.nextmillmedia.com/load-cookie.html?v=4'; +const SYNC_ENDPOINT = 'https://cookies.nextmillmedia.com/sync?'; const TIME_TO_LIVE = 360; const VIDEO_PARAMS = [ 'api', 'linearity', 'maxduration', 'mimes', 'minduration', 'placement', @@ -198,28 +198,24 @@ export const spec = { }, getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { - if (!syncOptions.iframeEnabled) { - return; - }; - - let syncurl = gdprConsent && gdprConsent.gdprApplies ? `${SYNC_ENDPOINT}&gdpr=1&gdpr_consent=${gdprConsent.consentString}` : SYNC_ENDPOINT; - - let bidders = []; - if (responses) { - _each(responses, (response) => { - if (!(response && response.body && response.body.ext && response.body.ext.responsetimemillis)) return; - _each(Object.keys(response.body.ext.responsetimemillis), b => bidders.push(b)); - }); - }; - - if (bidders.length) { - syncurl += `&bidders=${bidders.join(',')}`; - }; - - return [{ - type: 'iframe', - url: syncurl - }]; + const pixels = []; + let syncUrl = SYNC_ENDPOINT; + + if (gdprConsent && gdprConsent.gdprApplies) { + syncUrl += 'gdpr=1&gdpr_consent=' + gdprConsent.consentString; + } + if (uspConsent) { + syncUrl += 'us_privacy=' + uspConsent; + } + + if (syncOptions.iframeEnabled) { + pixels.push({type: 'iframe', url: syncUrl + 'type=iframe'}); + } + if (syncOptions.pixelEnabled) { + pixels.push({type: 'image', url: syncUrl + 'type=image'}); + } + + return pixels; }, }; diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 36920245070..2b5ae801489 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -148,14 +148,15 @@ describe('nextMillenniumBidAdapterTests', function() { it('Test getUserSyncs function', function () { const syncOptions = { - 'iframeEnabled': true + 'iframeEnabled': false, + 'pixelEnabled': true } const userSync = spec.getUserSyncs(syncOptions); expect(userSync).to.be.an('array').with.lengthOf(1); expect(userSync[0].type).to.exist; expect(userSync[0].url).to.exist; - expect(userSync[0].type).to.be.equal('iframe'); - expect(userSync[0].url).to.be.equal('https://statics.nextmillmedia.com/load-cookie.html?v=4'); + expect(userSync[0].type).to.be.equal('image'); + expect(userSync[0].url).to.be.equal('https://cookies.nextmillmedia.com/sync?type=image'); }); it('validate_response_params', function() { From cb766ccf4da3a316bc20c0440f1577e1f5f1590a Mon Sep 17 00:00:00 2001 From: ahmadlob <109217988+ahmadlob@users.noreply.github.com> Date: Tue, 15 Nov 2022 15:11:52 +0200 Subject: [PATCH 439/569] Taboola Bid Adapter: unsupport dynamic endpoint (#9237) * create taboola adapter * create taboola adapter md * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * update the md * update the Maintainer email * * update MD page * refactor code for better readability * small fix in UT * * add privacy to the request builder * add relevant Ut * small fixes in UT * * code refactoring + add more accurate way to get page url and referer * add relevant Ut * small fixes in md * * code refactoring + gte user id * add relevant Ut * small fixes * * code refactoring + gte user id * add relevant Ut * small fixes * * update end point url * update UT * Update banner End point structure * small fixes + update epi url * remove the destruction from the bidResponse property * (update the unit tests) remove the destruction from the bidResponse property * fix tests * fix tests - run stubs on each test * rerun because of another adapter flaky test * rerun because of another adapter flaky test * fix cors issue, switch between height, width position * update badv, bcat to be based in the ortb2 to support prebid 7 new protocols + update Ut * retry run circleci * retry run circleci * pull from upstream update md (placement + pub ) * update badv, bcat UT * rerun build * rerun build * support storageAllowed restriction on unit tests for prebid 7 * create taboola adapter * create taboola adapter md * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * taboolaBidAdapter.js - small fixes taboolaBidAdapter_spec.js - new UT * update the md * update the Maintainer email * * update MD page * refactor code for better readability * small fix in UT * * add privacy to the request builder * add relevant Ut * small fixes in UT * * code refactoring + add more accurate way to get page url and referer * add relevant Ut * small fixes in md * * code refactoring + gte user id * add relevant Ut * small fixes * * code refactoring + gte user id * add relevant Ut * small fixes * * update end point url * update UT * Update banner End point structure * small fixes + update epi url * remove the destruction from the bidResponse property * (update the unit tests) remove the destruction from the bidResponse property * fix tests * fix tests - run stubs on each test * rerun because of another adapter flaky test * rerun because of another adapter flaky test * fix cors issue, switch between height, width position * update badv, bcat to be based in the ortb2 to support prebid 7 new protocols + update Ut * retry run circleci * retry run circleci * pull from upstream update md (placement + pub ) * update badv, bcat UT * rerun build * rerun build * support storageAllowed restriction on unit tests for prebid 7 * support storageAllowed restriction on unit tests for prebid 7 * add it also to the aftereach * add it also to the aftereach * change the api endpoint https protocol * update Taboola prebid documentation: tagId, publisherId, bidFloor. * update bid response ttl to 60 seconds. * update Taboola prebid documentation. * update-ttl-passing * Update taboolaBidAdapter_spec.js * add fallback default value in case of null * add semicolons * . * . * . * .. * ... * support-dynamic-endpoint-url * support-dynamic-endpoint-url * support-dynamic-endpoint-url * support-dynamic-endpoint-url * support-dynamic-endpoint-url * support-dynamic-endpoint-url * support-dynamic-endpoint-url * optional-chaining * set netRevenue to true. * fix bid response in case we have multiple impressions * add test * fix lint * unsupport-dynamic-endpoint Co-authored-by: Michael Co-authored-by: mikiz <31058500+mikizi@users.noreply.github.com> Co-authored-by: jenny.l Co-authored-by: jennylt <48404417+jennylt@users.noreply.github.com> Co-authored-by: Shakhal Levinson Co-authored-by: shakhaltb <108469576+shakhaltb@users.noreply.github.com> --- modules/taboolaBidAdapter.js | 6 +- modules/taboolaBidAdapter.md | 2 - test/spec/modules/taboolaBidAdapter_spec.js | 77 --------------------- 3 files changed, 3 insertions(+), 82 deletions(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index d4bf6623f9b..9bd42651796 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -9,7 +9,7 @@ import {getStorageManager} from '../src/storageManager.js'; const BIDDER_CODE = 'taboola'; const GVLID = 42; const CURRENCY = 'USD'; -export const END_POINT_URL = 'https://hb.bidder.taboola.com/TaboolaHBOpenRTBRequestHandlerServlet'; +export const END_POINT_URL = 'https://display.bidder.taboola.com/OpenRTB/TaboolaHB/auction'; const USER_ID = 'user-id'; const STORAGE_KEY = `taboola global:${USER_ID}`; const COOKIE_KEY = 'trc_cookie_storage'; @@ -80,7 +80,7 @@ export const spec = { buildRequests: (validBidRequests, bidderRequest) => { const [bidRequest] = validBidRequests; const {refererInfo, gdprConsent = {}, uspConsent} = bidderRequest; - const {publisherId, endpointUrl} = bidRequest.params; + const {publisherId} = bidRequest.params; const site = getSiteProperties(bidRequest.params, refererInfo); const device = {ua: navigator.userAgent}; const imps = getImps(validBidRequests); @@ -124,7 +124,7 @@ export const spec = { regs }; - const url = [endpointUrl || END_POINT_URL, publisherId].join('/'); + const url = [END_POINT_URL, publisherId].join('/'); return { url, diff --git a/modules/taboolaBidAdapter.md b/modules/taboolaBidAdapter.md index d63616d23a9..79538d0d48b 100644 --- a/modules/taboolaBidAdapter.md +++ b/modules/taboolaBidAdapter.md @@ -31,7 +31,6 @@ The Taboola Bidding adapter requires setup before beginning. Please contact us o bidfloor: 0.25, // Optional - default is null bcat: ['IAB1-1'], // Optional - default is [] badv: ['example.com'], // Optional - default is [] - endpointUrl: ['https://example.com'] // Optional } }] }]; @@ -46,6 +45,5 @@ The Taboola Bidding adapter requires setup before beginning. Please contact us o | `bcat` | optional | List of blocked advertiser categories (IAB) | `['IAB1-1']` | `Array` | | `badv` | optional | Blocked Advertiser Domains | `'example.com'` | `String Url` | | `bidfloor` | optional | CPM bid floor | `0.25` | `Float` | -| `endpointUrl` | optional | Endpoint Url (only if provided by Taboola) | `https://example.com` | `String` | diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 9af11363957..c0b16ac40fc 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -89,45 +89,6 @@ describe('Taboola Adapter', function () { } expect(spec.isBidRequestValid(bid)).to.equal(true) }) - - it('should succeed when url is null', function () { - const bid = { - bidder: 'taboola', - params: { - publisherId: 'publisherId', - tagId: 'below the article', - endpointUrl: null - }, - ...displayBidRequestParams - } - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) - - it('should succeed when url is empty string', function () { - const bid = { - bidder: 'taboola', - params: { - publisherId: 'publisherId', - tagId: 'below the article', - endpointUrl: '' - }, - ...displayBidRequestParams - } - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) - - it('should succeed when url is filled', function () { - const bid = { - bidder: 'taboola', - params: { - publisherId: 'publisherId', - tagId: 'below the article', - endpointUrl: 'https://example.com' - }, - ...displayBidRequestParams - } - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) }) describe('buildRequests', function () { @@ -189,44 +150,6 @@ describe('Taboola Adapter', function () { expect(res.data).to.deep.equal(JSON.stringify(expectedData)); }); - it('should fill the url when it is passed', function () { - const commonBidRequestWithUrl = { - bidder: 'taboola', - params: { - publisherId: 'publisherId', - tagId: 'placement name', - endpointUrl: 'https://example.com' - }, - bidId: 'aa43860a-4644-442a-b5e0-93f268cs4d19', - auctionId: '65746dca-26f3-4186-be13-dfa63469b1b7', - } - let defaultBidRequestWithUrl = { - ...commonBidRequestWithUrl, - ...displayBidRequestParams, - } - const res = spec.buildRequests([defaultBidRequestWithUrl], commonBidderRequest); - expect(res.url).to.equal(`${commonBidRequestWithUrl.params.endpointUrl}/${commonBidRequest.params.publisherId}`); - }) - - it('should fill default url when url param is empty string', function () { - const commonBidRequestWithUrl = { - bidder: 'taboola', - params: { - publisherId: 'publisherId', - tagId: 'placement name', - endpointUrl: '' - }, - bidId: 'aa43860a-4644-442a-b5e0-93f268cs4d19', - auctionId: '65746dca-26f3-4186-be13-dfa63469b1b7', - } - let defaultBidRequestWithUrl = { - ...commonBidRequestWithUrl, - ...displayBidRequestParams, - } - const res = spec.buildRequests([defaultBidRequestWithUrl], commonBidderRequest); - expect(res.url).to.equal(`${END_POINT_URL}/${commonBidRequestWithUrl.params.publisherId}`); - }) - it('should pass optional parameters in request', function () { const optionalParams = { bidfloor: 0.25, From e717dcdfdead403d1c0369259c63480cd3b85b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= <88041828+smart-adserver@users.noreply.github.com> Date: Tue, 15 Nov 2022 14:42:50 +0100 Subject: [PATCH 440/569] Smartadserver Bid Adapter: discard bid response if ad and adUrl empty (#9213) * Discard bid response if ad and adUrl empty * Restore removeAll in U.T. * Restore one more removeAll --- modules/smartadserverBidAdapter.js | 2 +- .../modules/smartadserverBidAdapter_spec.js | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 5d64cad27b5..b1a69407df4 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -209,7 +209,7 @@ export const spec = { const bidResponses = []; let response = serverResponse.body; try { - if (response && !response.isNoAd) { + if (response && !response.isNoAd && (response.ad || response.adUrl)) { const bidRequest = JSON.parse(bidRequestString.data); let bidResponse = { diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 727a7d7c1d6..18308481e1a 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -200,6 +200,27 @@ describe('Smart bid adapter tests', function () { }).to.not.throw(); }); + it('Should not nest response if ad and adUrl empty', () => { + const BID_RESPONSE_EMPTY = { + body: { + ad: null, + adUrl: null, + cpm: 0.92, + isNoAd: false + } + }; + + const request = spec.buildRequests(DEFAULT_PARAMS); + const bids = spec.interpretResponse(BID_RESPONSE_EMPTY, request[0]); + + expect(bids).to.have.lengthOf(0); + expect(() => { + spec.interpretResponse(BID_RESPONSE_EMPTY, { + data: 'invalid Json' + }); + }).to.not.throw(); + }); + it('Verify parse response', function () { const request = spec.buildRequests(DEFAULT_PARAMS); const bids = spec.interpretResponse(BID_RESPONSE, request[0]); From 39958eab94b751377ebd2dfdff5a98bb9f0b0c97 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 15 Nov 2022 19:39:59 +0530 Subject: [PATCH 441/569] updated the correct variable while setting cookie (#9234) Co-authored-by: pm-azhar-mulla --- modules/userId/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 8fdd4319dfc..fe47ac5c16d 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -240,7 +240,7 @@ export function setStoredValue(submodule, value) { const valueStr = isPlainObject(value) ? JSON.stringify(value) : value; if (storage.type === COOKIE) { const setCookie = cookieSetter(submodule); - setCookie(null, value, expiresStr); + setCookie(null, valueStr, expiresStr); if (typeof storage.refreshInSeconds === 'number') { setCookie('_last', new Date().toUTCString(), expiresStr); } From 4e6904202a00be39c67845c74b55c113f688adef Mon Sep 17 00:00:00 2001 From: Gino <53079123+Skylinar@users.noreply.github.com> Date: Tue, 15 Nov 2022 16:34:20 +0100 Subject: [PATCH 442/569] Smartx Bid Adapter: Add Schain support (#9244) * Add smartclipBidAdapter * smartxBidAdapter.js - removed unused variables, removed debug, added window before the outstream related functions * - made outstream player configurable * remove wrong named files * camelcase * fix * Out-Stream render update to SmartPlay 5.2 * ESlint fix * ESlint fix * ESlint fix * adjust tests, fixes * ESlint * adjusted desired bitrate examples * added bid.meta.advertiserDomains support * bug fix for numeric elementID outstream render * fix renderer url * support for floors module * bugfixes to be openRTB 2.5 compliant * update internal renderer usage * remove unused outstream_function logic * bugfix outstream options for default outstream renderer configuration * [PREB-10] fix empty title not configurable * add pbjs version * testing with outstream 5.3.0 * pbjs version into content.ext * made visibilityThreshold configurable * adjust position of pbjs version * Merge branch 'master' of https://github.com/prebid/Prebid.js into HEAD * update smartclip outstream player version to support outstream 6 release along with necessary config changes * Add support for schain * vacuuming Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini Co-authored-by: smartclip-adtech <65160328+smartclip-adtech@users.noreply.github.com> --- modules/smartxBidAdapter.js | 17 +++++++----- test/spec/modules/smartxBidAdapter_spec.js | 32 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index e5f71265e34..f8438a35000 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -67,7 +67,6 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (bidRequests, bidderRequest) { - // TODO: does the fallback make sense here? const page = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; const isPageSecure = !!page.match(/^https:/) @@ -197,6 +196,15 @@ export const spec = { userExt.fpc = pubcid; } + // Add schain object if available + if (bid && bid.schain) { + requestPayload['source'] = { + ext: { + schain: bid.schain + } + }; + } + // Only add the user object if it's not empty if (!isEmpty(userExt)) { requestPayload.user = { @@ -204,9 +212,7 @@ export const spec = { }; } - // requestPayload.user.ext.ver = pbjs.version; - - // Targeting + // Add targeting if (getBidIdParameter('data', bid.params.user)) { var targetingarr = []; for (var i = 0; i < bid.params.user.data.length; i++) { @@ -225,8 +231,6 @@ export const spec = { } } - // Todo: USER ID MODULE - requestPayload.user = { ext: userExt, data: targetingarr @@ -269,7 +273,6 @@ export const spec = { } /** * Make sure currency and price are the right ones - * TODO: what about the pre_market_bid partners sizes? */ _each(currentBidRequest.params.pre_market_bids, function (pmb) { if (pmb.deal_id == smartxBid.id) { diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index 58ce50efb8e..5bd08064c79 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -342,6 +342,38 @@ describe('The smartx adapter', function () { expect(request.data.imp[0].video.minduration).to.equal(3); expect(request.data.imp[0].video.maxduration).to.equal(15); }); + + it('should pass schain param', function () { + var request; + + bid.schain = { + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.source).to.deep.equal({ + ext: { + schain: { + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } + } + }) + }); }); describe('interpretResponse', function () { From ba3d37159214dc9915ba7e6c28b27033d6e27eb6 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Tue, 15 Nov 2022 16:12:47 -0500 Subject: [PATCH 443/569] Fix to merge site fpd into payload as opposed to overwriting (#9247) --- modules/ttdBidAdapter.js | 7 ++++--- test/spec/modules/ttdBidAdapter_spec.js | 13 +++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/ttdBidAdapter.js b/modules/ttdBidAdapter.js index a855c07dc86..56dc44827b7 100644 --- a/modules/ttdBidAdapter.js +++ b/modules/ttdBidAdapter.js @@ -132,14 +132,15 @@ function getUser(bidderRequest) { } function getSite(bidderRequest, firstPartyData) { - var site = { + var site = utils.mergeDeep({ page: utils.deepAccess(bidderRequest, 'refererInfo.page'), ref: utils.deepAccess(bidderRequest, 'refererInfo.ref'), publisher: { id: utils.deepAccess(bidderRequest, 'bids.0.params.publisherId'), }, - ...firstPartyData.site - }; + }, + firstPartyData.site + ); var publisherDomain = bidderRequest.refererInfo.domain; if (publisherDomain) { diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index 60c00f4e4b9..346f1ef88f6 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -310,6 +310,19 @@ describe('ttdBidAdapter', function () { expect(requestBody.imp[0].banner.expdir).to.equal(expdir); }); + it('merges first party site data', function () { + const ortb2 = { + site: { + publisher: { + domain: 'https://foo.bar', + } + } + }; + const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + config.resetConfig(); + expect(requestBody.site.publisher).to.deep.equal({domain: 'https://foo.bar', id: '13144370'}); + }); + it('sets keywords properly if sent', function () { const ortb2 = { site: { From 625d2755d23187a92af0a5e7b5afe16f0c30a9d3 Mon Sep 17 00:00:00 2001 From: BaronJHYu <254878848@qq.com> Date: Wed, 16 Nov 2022 21:00:33 +0800 Subject: [PATCH 444/569] Discovery Bid Adapter : parameter updates (#9249) * Mediago Bid Adapter:new adapter * remove console * change spec file to fix CircleCI * change spec file to fix CircleCI * change spec file * Update mediagoBidAdapter.js * Update mediagoBidAdapter.js * rerun CurcleCi * update mediagoBidAdapter * update discoveryBidAdapter Co-authored-by: BaronYu --- modules/discoveryBidAdapter.js | 55 +++++++++++++------ modules/discoveryBidAdapter.md | 13 +++-- test/spec/modules/discoveryBidAdapter_spec.js | 17 +++--- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index 4f22a41cf9f..fab3920efcc 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -12,7 +12,8 @@ let itemMaps = {}; const MEDIATYPE = [BANNER, NATIVE]; /* ----- _ss_pp_id:start ------ */ -const COOKIE_KEY_MGUID = '_ss_pp_id'; +const COOKIE_KEY_SSPPID = '_ss_pp_id'; +const COOKIE_KEY_MGUID = '__mguid_'; const NATIVERET = { id: 'id', @@ -58,14 +59,20 @@ const NATIVERET = { * @return {string} */ const getUserID = () => { - const i = storage.getCookie(COOKIE_KEY_MGUID); + let idd = storage.getCookie(COOKIE_KEY_SSPPID); + let idm = storage.getCookie(COOKIE_KEY_MGUID); - if (i === null) { + if (idd && !idm) { + idm = idd + } else if (idm && !idd) { + idd = idm + } else if (!idd && !idm) { const uuid = utils.generateUUID(); storage.setCookie(COOKIE_KEY_MGUID, uuid); + storage.setCookie(COOKIE_KEY_SSPPID, uuid); return uuid; } - return i; + return idd; }; /* ----- _ss_pp_id:end ------ */ @@ -80,7 +87,6 @@ function getKv(obj, ...keys) { let o = obj; for (let key of keys) { - // console.log(key, o); if (o && o[key]) { o = o[key]; } else { @@ -222,7 +228,7 @@ function getItems(validBidRequests, bidderRequest) { let id = '' + (i + 1); if (mediaTypes.native) { - ret = {...NATIVERET, ...{id, bidFloor}} + ret = { ...NATIVERET, ...{ id, bidFloor } } } // banner if (mediaTypes.banner) { @@ -249,6 +255,7 @@ function getItems(validBidRequests, bidderRequest) { pos: 1, }, ext: {}, + tagid: globals['tagid'], }; } itemMaps[id] = { @@ -273,14 +280,19 @@ function getParam(validBidRequests, bidderRequest) { let auctionId = getKv(bidderRequest, 'auctionId'); let items = getItems(validBidRequests, bidderRequest); - const location = utils.deepAccess(bidderRequest, 'refererInfo.referer'); - const timeout = bidderRequest.timeout || 2000; + const domain = utils.deepAccess(bidderRequest, 'refererInfo.domain') || document.domain; + const location = utils.deepAccess(bidderRequest, 'refererInfo.referer'); + const page = utils.deepAccess(bidderRequest, 'refererInfo.page'); + const referer = utils.deepAccess(bidderRequest, 'refererInfo.ref'); + if (items && items.length) { let c = { id: 'pp_hbjs_' + auctionId, at: 1, + bcat: globals['bcat'], + badv: globals['adv'], cur: ['USD'], device: { connectiontype: 0, @@ -295,16 +307,16 @@ function getParam(validBidRequests, bidderRequest) { }, tmax: timeout, site: { - name: globals['media'], - domain: globals['media'], - page: location, - ref: location, + name: domain, + domain: domain, + page: page || location, + ref: referer, mobile: isMobile, cat: [], // todo publisher: { + id: globals['publisher'] // todo - id: globals['media'], - name: globals['media'], + // name: xxx }, }, imp: items, @@ -329,10 +341,19 @@ export const spec = { if (bid.params.token) { globals['token'] = bid.params.token; } - if (bid.params.media) { - globals['media'] = bid.params.media; + if (bid.params.publisher) { + globals['publisher'] = bid.params.publisher; + } + if (bid.params.tagid) { + globals['tagid'] = bid.params.tagid; + } + if (bid.params.bcat) { + globals['bcat'] = Array.isArray(bid.params.bcat) ? bid.params.bcat : []; + } + if (bid.params.badv) { + globals['badv'] = Array.isArray(bid.params.badv) ? bid.params.badv : []; } - return !!(bid.params.token && bid.params.media); + return !!(bid.params.token && bid.params.publisher && bid.params.tagid); }, /** diff --git a/modules/discoveryBidAdapter.md b/modules/discoveryBidAdapter.md index 6e7197863a5..e951b0b7448 100644 --- a/modules/discoveryBidAdapter.md +++ b/modules/discoveryBidAdapter.md @@ -1,15 +1,15 @@ # Overview ``` -Module Name: DiscoveryDSP Bid Adapter +Module Name: discovery Bid Adapter Module Type: Bidder Adapter ``` # Description -Module that connects to popIn's demand sources +Module that connects to popIn's demand sources. -The DiscoveryDSP Bidding adapter requires setup before beginning. Please contact us at +The discovery Bidding adapter requires setup before beginning. Please contact us at # Test Parameters ``` @@ -32,7 +32,8 @@ The DiscoveryDSP Bidding adapter requires setup before beginning. Please contact bidder: "discovery", params: { token: "a1b067897e4ae093d1f94261e0ddc6c9", - media: 'test_media' // your media host + tagid: 'test_tagid', + publisher: 'test_publisher' }, }, ], @@ -45,13 +46,13 @@ The DiscoveryDSP Bidding adapter requires setup before beginning. Please contact sizes: [[300, 250]], }, }, - // Replace this object to test a new adapter! bids: [ { bidder: "discovery", params: { token: "d0f4902b616cc5c38cbe0a08676d0ed9", - media: 'test_media' // your media host + tagid: 'test_tagid', + publisher: 'test_publisher' }, }, ], diff --git a/test/spec/modules/discoveryBidAdapter_spec.js b/test/spec/modules/discoveryBidAdapter_spec.js index a30afd96ad1..078add73046 100644 --- a/test/spec/modules/discoveryBidAdapter_spec.js +++ b/test/spec/modules/discoveryBidAdapter_spec.js @@ -1,14 +1,14 @@ import { expect } from 'chai'; import { spec } from 'modules/discoveryBidAdapter.js'; -describe('DiscoveryDSP:BidAdapterTests', function () { +describe('discovery:BidAdapterTests', function () { let bidRequestData = { - bidderCode: 'DiscoveryDSP', + bidderCode: 'discovery', auctionId: 'ff66e39e-4075-4d18-9854-56fde9b879ac', bidderRequestId: '4fec04e87ad785', bids: [ { - bidder: 'DiscoveryDSP', + bidder: 'discovery', params: { token: 'd0f4902b616cc5c38cbe0a08676d0ed9', }, @@ -32,25 +32,26 @@ describe('DiscoveryDSP:BidAdapterTests', function () { }; let request = []; - it('DiscoveryDSP:validate_pub_params', function () { + it('discovery:validate_pub_params', function () { expect( spec.isBidRequestValid({ - bidder: 'DiscoveryDSP', + bidder: 'discovery', params: { token: ['d0f4902b616cc5c38cbe0a08676d0ed9'], - media: ['test_media'] + tagid: ['test_tagid'], + publisher: ['test_publisher'] }, }) ).to.equal(true); }); - it('DiscoveryDSP:validate_generated_params', function () { + it('discovery:validate_generated_params', function () { request = spec.buildRequests(bidRequestData.bids, bidRequestData); let req_data = JSON.parse(request.data); expect(req_data.imp).to.have.lengthOf(1); }); - it('DiscoveryDSP:validate_response_params', function () { + it('discovery:validate_response_params', function () { let tempAdm = '' tempAdm += '%3Cscr'; tempAdm += 'ipt%3E'; From e7b981d51636bef2a56237ccb36d79913432bccc Mon Sep 17 00:00:00 2001 From: mcourouble <117740024+mcourouble@users.noreply.github.com> Date: Wed, 16 Nov 2022 14:10:27 +0100 Subject: [PATCH 445/569] Smartadserver Bid Adapter: add support for SDA user and site (#9231) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Smartadserver Bid Adapter: Add support for SDA user and site * Smartadserver Bid Adapter: Fix SDA support getConfig and add to unit testing Co-authored-by: Krzysztof Sokół <88041828+smart-adserver@users.noreply.github.com> --- modules/smartadserverBidAdapter.js | 6 +- .../modules/smartadserverBidAdapter_spec.js | 59 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index b1a69407df4..6ff0e592542 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -133,6 +133,8 @@ export const spec = { // use bidderRequest.bids[] to get bidder-dependent request info const adServerCurrency = config.getConfig('currency.adServerCurrency'); + const sellerDefinedAudience = deepAccess(bidderRequest, 'ortb2.user.data', config.getAnyConfig('ortb2.user.data')); + const sellerDefinedContext = deepAccess(bidderRequest, 'ortb2.site.content.data', config.getAnyConfig('ortb2.site.content.data')); // pull requested transaction ID from bidderRequest.bids[].transactionId return validBidRequests.reduce((bidRequests, bid) => { @@ -154,7 +156,9 @@ export const spec = { timeout: config.getConfig('bidderTimeout'), bidId: bid.bidId, prebidVersion: '$prebid.version$', - schain: spec.serializeSupplyChain(bid.schain) + schain: spec.serializeSupplyChain(bid.schain), + sda: sellerDefinedAudience, + sdc: sellerDefinedContext }; if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 18308481e1a..db61983c9c9 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -158,12 +158,44 @@ describe('Smart bid adapter tests', function () { } }; + var sellerDefinedAudience = [ + { + 'name': 'hearst.com', + 'ext': { 'segtax': 1 }, + 'segment': [ + { 'id': '1001' }, + { 'id': '1002' } + ] + } + ]; + + var sellerDefinedContext = [ + { + 'name': 'cnn.com', + 'ext': { 'segtax': 2 }, + 'segment': [ + { 'id': '2002' } + ] + } + ]; + it('Verify build request', function () { config.setConfig({ 'currency': { 'adServerCurrency': 'EUR' + }, + ortb2: { + 'user': { + 'data': sellerDefinedAudience + }, + 'site': { + 'content': { + 'data': sellerDefinedContext + } + } } }); + const request = spec.buildRequests(DEFAULT_PARAMS); expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1'); expect(request[0]).to.have.property('method').and.to.equal('POST'); @@ -186,6 +218,8 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('buid').and.to.equal('7569'); expect(requestContent).to.have.property('appname').and.to.equal('Mozilla'); expect(requestContent).to.have.property('ckid').and.to.equal(42); + expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience); + expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext); }); it('Verify parse response with no ad', function () { @@ -358,6 +392,7 @@ describe('Smart bid adapter tests', function () { describe('gdpr tests', function () { afterEach(function () { + config.setConfig({ ortb2: undefined }); config.resetConfig(); $$PREBID_GLOBAL$$.requestBids.removeAll(); }); @@ -489,6 +524,16 @@ describe('Smart bid adapter tests', function () { config.setConfig({ 'currency': { 'adServerCurrency': 'EUR' + }, + ortb2: { + 'user': { + 'data': sellerDefinedAudience + }, + 'site': { + 'content': { + 'data': sellerDefinedContext + } + } } }); const request = spec.buildRequests(INSTREAM_DEFAULT_PARAMS); @@ -507,6 +552,8 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('buid').and.to.equal('7569'); expect(requestContent).to.have.property('appname').and.to.equal('Mozilla'); expect(requestContent).to.have.property('ckid').and.to.equal(42); + expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience); + expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext); expect(requestContent).to.have.property('isVideo').and.to.equal(true); expect(requestContent).to.have.property('videoData'); expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6); @@ -748,6 +795,16 @@ describe('Smart bid adapter tests', function () { config.setConfig({ 'currency': { 'adServerCurrency': 'EUR' + }, + ortb2: { + 'user': { + 'data': sellerDefinedAudience + }, + 'site': { + 'content': { + 'data': sellerDefinedContext + } + } } }); const request = spec.buildRequests(OUTSTREAM_DEFAULT_PARAMS); @@ -766,6 +823,8 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('buid').and.to.equal('7579'); expect(requestContent).to.have.property('appname').and.to.equal('Mozilla'); expect(requestContent).to.have.property('ckid').and.to.equal(43); + expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience); + expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext); expect(requestContent).to.have.property('isVideo').and.to.equal(false); expect(requestContent).to.have.property('videoData'); expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(7); From f5e6c61404c93b1a055c81071bebaf277b15e4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Wed, 16 Nov 2022 18:13:38 +0200 Subject: [PATCH 446/569] VidazooBidAdapter: get bid floor using `bid.getFloor` (#9238) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * added bid.getFloor handler Co-authored-by: roman Co-authored-by: Saar Amrani <89377180+saar120@users.noreply.github.com> Co-authored-by: Saar Amrani --- modules/vidazooBidAdapter.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index fa44bde74f1..ac74dd18405 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,4 +1,4 @@ -import {_each, deepAccess, parseSizesInput, parseUrl, uniques} from '../src/utils.js'; +import { _each, deepAccess, parseSizesInput, parseUrl, uniques, isFn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -59,7 +59,8 @@ function isBidRequestValid(bid) { function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const { params, bidId, userId, adUnitCode, schain } = bid; - const { bidFloor, ext } = params; + const { ext } = params; + let { bidFloor } = params; const hashUrl = hashCode(topWindowUrl); const dealId = getNextDealId(hashUrl); const uniqueDealId = getUniqueDealId(hashUrl); @@ -69,6 +70,18 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const subDomain = extractSubDomain(params); const ptrace = getCacheOpt(); + if (isFn(bid.getFloor)) { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + + if (floorInfo.currency === 'USD') { + bidFloor = floorInfo.floor; + } + } + let data = { url: encodeURIComponent(topWindowUrl), uqs: getTopWindowQueryParams(), From c3f789bb64a2d298b80b747ced74feea4cd0ba57 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 17 Nov 2022 00:55:05 +0300 Subject: [PATCH 447/569] Viqeo Bid Adapter: initial adapter release (#8920) * add viqeo prebid adapter * added bid params to docs * updated to Outstream * updated to Outstream (tests) --- modules/viqeoBidAdapter.js | 180 ++++++++++++++++++++++ modules/viqeoBidAdapter.md | 56 +++++++ test/spec/modules/viqeoBidAdapter_spec.js | 124 +++++++++++++++ 3 files changed, 360 insertions(+) create mode 100644 modules/viqeoBidAdapter.js create mode 100644 modules/viqeoBidAdapter.md create mode 100644 test/spec/modules/viqeoBidAdapter_spec.js diff --git a/modules/viqeoBidAdapter.js b/modules/viqeoBidAdapter.js new file mode 100644 index 00000000000..5762a794c8e --- /dev/null +++ b/modules/viqeoBidAdapter.js @@ -0,0 +1,180 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {logError, logInfo, _each, mergeDeep, isFn, isNumber, isPlainObject} from '../src/utils.js' +import {VIDEO} from '../src/mediaTypes.js'; +import {Renderer} from '../src/Renderer.js'; + +const BIDDER_CODE = 'viqeo'; +const DEFAULT_MIMES = ['application/javascript']; +const VIQEO_ENDPOINT = 'https://ads.betweendigital.com/openrtb_bid'; +const RENDERER_URL = 'https://cdn.viqeo.tv/js/vq_starter.js'; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_SSPID = 44697; + +function getBidFloor(bid) { + const {floor, currency} = bid.params; + const curr = currency || DEFAULT_CURRENCY; + if (!isFn(bid.getFloor)) { + return {floor: isNumber(floor) ? floor : 0, currency: curr}; + } + const floorInfo = bid.getFloor({currency: curr, mediaType: VIDEO, size: '*'}); + if (isPlainObject(floorInfo) && isNumber(floorInfo.floor) && floorInfo.currency === curr) { + return floorInfo; + } + return {floor: floor || 0, currency: currency || DEFAULT_CURRENCY}; +} + +function getVideoTargetingParams({mediaTypes: {video}}) { + const result = {}; + Object.keys(Object(video)) + .forEach(key => { + if (key === 'playerSize') { + result.w = video.playerSize[0][0]; + result.h = video.playerSize[0][1]; + } else if (key !== 'context') { + result[key] = video[key]; + } + }) + return result; +} + +/** + * @type {BidderSpec} + */ +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [VIDEO], + /** + * @param {BidRequest} bidRequest The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: ({params}) => { + if (!params) { + logError('failed validation: params not declared'); + return false; + } + if (!params.user && !params.user?.buyeruid) { + logError('failed validation: user.buyeruid not declared'); + return false; + } + if (!params.playerOptions) { + logError('failed validation: playerOptions not declared'); + return false; + } + const {profileId, videoId, playerId} = params.playerOptions; + if (!profileId) { + logError('failed validation: profileId not declared'); + return false; + } + if (!videoId && !playerId) { + logError('failed validation: videoId or playerId not declared'); + return false; + } + return true; + }, + /** + * @param validBidRequests {BidRequest[]} + * @returns {ServerRequest[]} + */ + buildRequests: (validBidRequests) => { + logInfo('validBidRequests', validBidRequests); + const bidRequests = []; + _each(validBidRequests, (bid, i) => { + const { + params: {test, sspId, endpointUrl}, + mediaTypes: {video}, + } = bid; + const ortb2 = bid.ortb2 || {}; + const user = bid.params.user || {}; + const device = bid.params.device || {}; + const site = bid.params.site || {}; + const w = window; + const floorInfo = getBidFloor(bid); + const data = { + id: bid.bidId, + test, + imp: [{ + id: `${i}`, + tagid: bid.adUnitCode, + video: { + ...getVideoTargetingParams(bid), + mimes: video.mimes || DEFAULT_MIMES, + }, + bidfloor: floorInfo.floor, + bidfloorcur: floorInfo.currency, + secure: 1 + }], + site: test === 1 ? { + page: 'https://viqeo.tv', + domain: 'viqeo.tv' + } : mergeDeep({ + domain: w.location.hostname, + page: w.location.href + }, ortb2.site, site), + device: mergeDeep({ + w: w.screen.width, + h: w.screen.height, + ua: w.navigator.userAgent, + }, ortb2.device, device), + user: mergeDeep({...user}, ortb2.user), + app: bid.params.app, + }; + bidRequests.push({ + url: endpointUrl || `${VIQEO_ENDPOINT}/?sspId=${sspId || DEFAULT_SSPID}`, + method: 'POST', + data, + bids: validBidRequests, + }); + }); + return bidRequests; + }, + /** + * @param {ServerResponse} serverResponse + * @param {BidRequest} bidRequests + * @return {Bid[]} + */ + interpretResponse: (serverResponse, bidRequests) => { + logInfo('serverResponse', serverResponse); + const bidResponses = []; + if (!serverResponse || !serverResponse.body) { + logError('empty response'); + return []; + } + try { + const {id, seatbid, cur} = serverResponse.body; + _each(seatbid, (sb) => { + const {bid} = sb; + _each(bid, (b) => { + const bidRequest = bidRequests.bids.find(({bidId}) => bidId === id); + const renderer = Renderer.install({ + url: bidRequest?.params?.renderUrl || RENDERER_URL, + }); + renderer.setRender((bid) => { + if (window.VIQEO) { + window.VIQEO.renderPrebid(bid); + } else { + logError('failed get window.VIQEO'); + } + }); + bidResponses.push({ + requestId: id, + currency: cur, + cpm: b.price, + ttl: b.exp, + netRevenue: true, + creativeId: b.cid, + width: b.w || bidRequest?.mediaTypes[VIDEO].playerSize[0][0], + height: b.h || bidRequest?.mediaTypes[VIDEO].playerSize[0][1], + vastXml: b.adm, + vastUrl: b.nurl, + mediaType: VIDEO, + renderer, + }) + }) + }); + } catch (error) { + logError(error); + } + return bidResponses; + }, +} +registerBidder(spec); diff --git a/modules/viqeoBidAdapter.md b/modules/viqeoBidAdapter.md new file mode 100644 index 00000000000..b4a020bf057 --- /dev/null +++ b/modules/viqeoBidAdapter.md @@ -0,0 +1,56 @@ +# Overview + +**Module Name**: Viqeo Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: muravjovv1@gmail.com + +# Description + +Viqeo Bidder Adapter for Prebid.js. About: https://viqeo.tv/ + +### Bid params + +{: .table .table-bordered .table-striped } +| Name | Scope | Description | Example | Type | +|-----------------------------|----------|----------------------------------------------------------------------------------------------------------------------------|--------------------------|-----------| +| `user` | required | The object containing user data (See OpenRTB spec) | `user: {}` | `object` | +| `user.buyeruid` | required | User id | `"12345"` | `string` | +| `playerOptions` | required | The object containing Viqeo player options | `playerOptions: {}` | `object` | +| `playerOptions.profileId` | required | Viqeo profile id | `1382` | `number` | +| `playerOptions.videId` | optional | Viqeo video id | `"ed584da454c7205ca7e4"` | `string` | +| `playerOptions.playerId` | optional | Viqeo player id | `1` | `number` | +| `device` | optional | The object containing device data (See OpenRTB spec) | `device: {}` | `object` | +| `site` | optional | The object containing site data (See OpenRTB spec) | `site: {}` | `object` | +| `app` | optional | The object containing app data (See OpenRTB spec) | `app: {}` | `object` | +| `floor` | optional | Bid floor price | `0.5` | `number` | +| `currency` | optional | 3-letter ISO 4217 code defining the currency of the bid. | `EUR` | `string` | +| `test` | optional | Flag which will induce a sample bid response when true; only set to true for testing purposes (1 = true, 0 = false) | `1` | `integer` | +| `sspId` | optional | For debug, request id | `1` | `number` | +| `renderUrl` | optional | For debug, script player url | `"https://viqeo.tv"` | `string` | +| `endpointUrl` | optional | For debug, api endpoint | `"https://viqeo.tv"` | `string` | + +# Test Parameters +``` + var adUnits = [{ + code: 'your-slot', // use exactly the same code as your slot div id. + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480] + } + }, + bids: [{ + bidder: 'viqeo', + params: { + user: { + buyeruid: '1', + }, + playerOptions: { + videoId: 'ed584da454c7205ca7e4', + profileId: 1382, + }, + test: 1, + } + }] + }]; +``` diff --git a/test/spec/modules/viqeoBidAdapter_spec.js b/test/spec/modules/viqeoBidAdapter_spec.js new file mode 100644 index 00000000000..8f597318af9 --- /dev/null +++ b/test/spec/modules/viqeoBidAdapter_spec.js @@ -0,0 +1,124 @@ +import {expect} from 'chai'; +import {spec} from 'modules/viqeoBidAdapter'; + +describe('viqeoBidAdapter', function () { + it('minimal params', function () { + expect(spec.isBidRequestValid({ + bidder: 'viqeo', + params: { + user: { + buyeruid: '1', + }, + playerOptions: { + videoId: 'ed584da454c7205ca7e4', + profileId: 1382, + }, + }})).to.equal(true); + }); + it('minimal params no playerOptions', function () { + expect(spec.isBidRequestValid({ + bidder: 'viqeo', + params: { + currency: 'EUR', + }})).to.equal(false); + }); + it('build request check data', function () { + const bidRequestData = [{ + bidId: 'id1', + bidder: 'viqeo', + params: { + user: { + buyeruid: '1', + }, + currency: 'EUR', + floor: 0.5, + playerOptions: { + videoId: 'ed584da454c7205ca7e4', + profileId: 1382, + }, + }, + mediaTypes: { + video: { playerSize: [[240, 400]] } + }, + }]; + const request = spec.buildRequests(bidRequestData); + const requestData = request[0].data; + expect(requestData.id).to.equal('id1') + expect(requestData.imp[0].bidfloorcur).to.equal('EUR'); + expect(requestData.imp[0].bidfloor).to.equal(0.5); + expect(requestData.imp[0].video.w).to.equal(240); + expect(requestData.imp[0].video.h).to.equal(400); + expect(requestData.user.buyeruid).to.equal('1'); + }); + it('build request check url', function () { + const bidRequestData = [{ + bidder: 'viqeo', + params: { + playerOptions: { + videoId: 'ed584da454c7205ca7e4', + profileId: 1382, + }, + sspId: 42, + }, + mediaTypes: { + video: { playerSize: [[240, 400]] } + }, + }]; + const request = spec.buildRequests(bidRequestData); + expect(request[0].url).to.equal('https://ads.betweendigital.com/openrtb_bid/?sspId=42') + }); + it('response_params common case', function () { + const bidRequestData = { + bids: [{ + bidId: 'id1', + params: {}, + mediaTypes: { + video: { playerSize: [[240, 400]] } + }, + }], + }; + const serverResponse = { + body: { + id: 'id1', + cur: 'EUR', + seatbid: [{ + bid: [{ + cpm: 0.5, + ttl: 3600, + netRevenue: true, + creativeId: 'test1', + adm: '', + }], + }], + } + }; + const bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + }); + it('should set flooPrice to getFloor.floor value if it is greater than params.floor', function() { + const bidRequestData = [{ + bidId: 'id1', + bidder: 'viqeo', + params: { + currency: 'EUR', + floor: 0.5, + playerOptions: { + videoId: 'ed584da454c7205ca7e4', + profileId: 1382, + }, + }, + mediaTypes: { + video: { playerSize: [[240, 400]] } + }, + getFloor: () => { + return { + currency: 'EUR', + floor: 3.32 + } + }, + }]; + const request = spec.buildRequests(bidRequestData); + const requestData = request[0].data; + expect(requestData.imp[0].bidfloor).to.equal(3.32) + }); +}); From c7da527e4d38819bd1d73dfcd0da75600f649391 Mon Sep 17 00:00:00 2001 From: SebRobert Date: Thu, 17 Nov 2022 13:14:01 +0100 Subject: [PATCH 448/569] BeOp Bid Adapter : update keywords management (#9166) * Don't know why params are in an array in that bid object. Make it work for both if it is fixed later * Update params reading method to adapt to arrays for all params * Keywords have to be an array of string (#9) * Keywords have to be an array of string * Last check if isStr * Fix linting errors * Fix tests --- modules/beopBidAdapter.js | 19 ++++++++- test/spec/modules/beopBidAdapter_spec.js | 52 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index f8a53a293de..4ee5aaec5be 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, isArray, logWarn, triggerPixel, buildUrl, logInfo, getValue, getBidIdParameter } from '../src/utils.js'; +import { deepAccess, isArray, isStr, logWarn, triggerPixel, buildUrl, logInfo, getValue, getBidIdParameter } from '../src/utils.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; @@ -40,6 +40,19 @@ export const spec = { const pageUrl = getPageUrl(bidderRequest.refererInfo, window); const gdpr = bidderRequest.gdprConsent; const firstSlot = slots[0]; + const kwdsFromRequest = firstSlot.kwds; + let keywords = []; + if (kwdsFromRequest) { + if (isArray(kwdsFromRequest)) { + keywords = kwdsFromRequest; + } else if (isStr(kwdsFromRequest)) { + if (kwdsFromRequest.indexOf(',') != -1) { + keywords = kwdsFromRequest.split(',').map((e) => { return e.trim() }); + } else { + keywords.push(kwdsFromRequest); + } + } + } const payloadObject = { at: new Date().toString(), nid: firstSlot.nid, @@ -47,12 +60,13 @@ export const spec = { pid: firstSlot.pid, url: pageUrl, lang: (window.navigator.language || window.navigator.languages[0]), - kwds: bidderRequest.ortb2?.site?.keywords || [], + kwds: keywords, dbg: false, slts: slots, is_amp: deepAccess(bidderRequest, 'referrerInfo.isAmp'), tc_string: (gdpr && gdpr.gdprApplies) ? gdpr.consentString : null, }; + const payloadString = JSON.stringify(payloadObject); return { method: 'POST', @@ -129,6 +143,7 @@ function beOpRequestSlotsMaker(bid) { sizes: isArray(bannerSizes) ? bannerSizes : bid.sizes, flr: floor, pid: getValue(bid.params, 'accountId'), + kwds: getValue(bid.params, 'keywords'), nid: getValue(bid.params, 'networkId'), nptnid: getValue(bid.params, 'networkPartnerId'), bid: getBidIdParameter('bidId', bid), diff --git a/test/spec/modules/beopBidAdapter_spec.js b/test/spec/modules/beopBidAdapter_spec.js index c6001c3ba2e..ad096fd6526 100644 --- a/test/spec/modules/beopBidAdapter_spec.js +++ b/test/spec/modules/beopBidAdapter_spec.js @@ -216,4 +216,56 @@ describe('BeOp Bid Adapter tests', () => { expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.include('pid=5a8af500c9e77c00017e4cad'); }); }); + + describe('Ensure keywords is always array of string', function () { + let bidRequests = []; + afterEach(function () { + bidRequests = []; + }); + + it('should work with keywords as an array', function () { + let bid = Object.assign({}, validBid); + bid.params.keywords = ['a', 'b']; + bidRequests.push(bid); + config.setConfig({ + currency: { adServerCurrency: 'USD' } + }); + const request = spec.buildRequests(bidRequests, {}); + const payload = JSON.parse(request.data); + const url = request.url; + expect(payload.kwds).to.exist; + expect(payload.kwds).to.include('a'); + expect(payload.kwds).to.include('b'); + }); + + it('should work with keywords as a string', function () { + let bid = Object.assign({}, validBid); + bid.params.keywords = 'list of keywords'; + bidRequests.push(bid); + config.setConfig({ + currency: { adServerCurrency: 'USD' } + }); + const request = spec.buildRequests(bidRequests, {}); + const payload = JSON.parse(request.data); + const url = request.url; + expect(payload.kwds).to.exist; + expect(payload.kwds).to.include('list of keywords'); + }); + + it('should work with keywords as a string containing a comma', function () { + let bid = Object.assign({}, validBid); + bid.params.keywords = 'list, of, keywords'; + bidRequests.push(bid); + config.setConfig({ + currency: { adServerCurrency: 'USD' } + }); + const request = spec.buildRequests(bidRequests, {}); + const payload = JSON.parse(request.data); + const url = request.url; + expect(payload.kwds).to.exist; + expect(payload.kwds).to.include('list'); + expect(payload.kwds).to.include('of'); + expect(payload.kwds).to.include('keywords'); + }) + }) }); From c86f41b9eabef64615211cc417291a6fa76078df Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:33:40 +0300 Subject: [PATCH 449/569] TheMediaGrid: added withCriteo paramater to send criteo request with the /hbjson request (#9214) --- modules/gridBidAdapter.js | 462 +++++++++++++++++++++-- test/spec/modules/gridBidAdapter_spec.js | 290 +++++++++++++- 2 files changed, 729 insertions(+), 23 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 181ce0ebab2..3aaeaf73580 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -1,18 +1,38 @@ -import { isEmpty, deepAccess, logError, parseGPTSingleSizeArrayToRtbSize, generateUUID, mergeDeep, logWarn } from '../src/utils.js'; +import { + isEmpty, + deepAccess, + logError, + parseGPTSingleSizeArrayToRtbSize, + generateUUID, + mergeDeep, + logWarn, + parseUrl, isArray, isNumber +} from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; +import { find } from '../src/polyfill.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; + +const ADAPTER_VERSION_FOR_CRITEO_MODE = 34; +const CDB_ENDPOINT = 'https://bidder.criteo.com/cdb'; +const PROFILE_ID_INLINE = 207; +const SID_COOKIE_NAME = 'cto_sid'; +const IDCPY_COOKIE_NAME = 'cto_idcpy'; +const OPTOUT_COOKIE_NAME = 'cto_optout'; +const BUNDLE_COOKIE_NAME = 'cto_bundle'; + const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const USER_ID_KEY = 'tmguid'; const GVLID = 686; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; export const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); + const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', @@ -49,7 +69,7 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - return !!bid.params.uid; + return bid && Boolean(bid.params.uid || bid.params.secid); }, /** * Make a server request from the list of BidRequests. @@ -79,10 +99,20 @@ export const spec = { const imp = []; const bidsMap = {}; const requests = []; + const criteoBidsMap = {}; + const criteoBidRequests = []; const sources = []; const bidsArray = []; validBidRequests.forEach((bid) => { + const bidObject = { bid, savedPrebidBid: null }; + if (bid.params.withCriteo && criteoSpec.isBidRequestValid(bid)) { + criteoBidsMap[bid.bidId] = bidObject; + criteoBidRequests.push(bid); + } + if (!bid.params.uid && !bid.params.secid) { + return; + } if (!bidderRequestId) { bidderRequestId = bid.bidderRequestId; } @@ -100,7 +130,6 @@ export const spec = { } const { params: { uid, keywords, forceBidder, multiRequest }, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp } = bid; const { pubdata, secid, pubid, source, content: bidParamsContent } = bid.params; - bidsMap[bidId] = bid; const bidFloor = _getFloor(mediaTypes || {}, bid); const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; if (jwTargeting) { @@ -201,8 +230,9 @@ export const spec = { requests.push(request); sources.push(source); - bidsArray.push(bid); + bidsArray.push(bidObject); } else { + bidsMap[bidId] = bidObject; imp.push(impObj); } } @@ -377,6 +407,11 @@ export const spec = { } }); + const criteoRequest = criteoBidRequests.length && criteoSpec.buildRequests(criteoBidRequests, bidderRequest); + if (criteoRequest) { + criteoRequest.criteoBidsMap = criteoBidsMap; + } + return [...requests.map((req, i) => { let sp; const url = (endpoint || ENDPOINT_URL).replace(/[?&]sp=([^?&=]+)/, (i, found) => { @@ -391,14 +426,14 @@ export const spec = { method: 'POST', url: urlWithParams, data: JSON.stringify(req), - bid: bidsArray[i], + bidObject: bidsArray[i], }; }), ...(mainRequest ? [{ method: 'POST', url: endpoint || ENDPOINT_URL, data: JSON.stringify(mainRequest), bidsMap - }] : [])]; + }] : []), ...(criteoRequest ? [criteoRequest] : [])]; }, /** * Unpack the response from the server into a list of bids. @@ -409,25 +444,33 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse, bidRequest, RendererConst = Renderer) { - serverResponse = serverResponse && serverResponse.body; - const bidResponses = []; + if (bidRequest.criteoBidsMap && bidRequest.bidRequests) { + const criteoBids = criteoSpec.interpretResponse(serverResponse, bidRequest); + return criteoBids.filter((bid) => { + const { savedPrebidBid } = bidRequest.criteoBidsMap[bid.requestId] || {}; + return canPublishResponse(bid.cpm, savedPrebidBid && savedPrebidBid.cpm); + }); + } else { + serverResponse = serverResponse && serverResponse.body; + const bidResponses = []; - let errorMessage; + let errorMessage; - if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; - else if (serverResponse.seatbid && !serverResponse.seatbid.length) { - errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; - } + if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; + else if (serverResponse.seatbid && !serverResponse.seatbid.length) { + errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; + } - const bidderCode = this.forceBidderName || this.code; + const bidderCode = this.forceBidderName || this.code; - if (!errorMessage && serverResponse.seatbid) { - serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidRequest, bidResponses, RendererConst, bidderCode); - }); + if (!errorMessage && serverResponse.seatbid) { + serverResponse.seatbid.forEach(respItem => { + _addBidResponse(_getBidFromResponse(respItem), bidRequest, bidResponses, RendererConst, bidderCode); + }); + } + if (errorMessage) logError(errorMessage); + return bidResponses; } - if (errorMessage) logError(errorMessage); - return bidResponses; }, getUserSyncs: function (...args) { const [syncOptions,, gdprConsent, uspConsent] = args; @@ -502,8 +545,9 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst, bid if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!errorMessage && !serverBid.adm && !serverBid.nurl) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { - const bid = bidRequest.bidsMap ? bidRequest.bidsMap[serverBid.impid] : bidRequest.bid; - if (bid) { + const bidObject = bidRequest.bidsMap ? bidRequest.bidsMap[serverBid.impid] : bidRequest.bidObject; + const { bid, savedPrebidBid } = bidObject || {}; + if (bid && canPublishResponse(serverBid.price, savedPrebidBid && savedPrebidBid.cpm)) { const bidResponse = { requestId: bid.bidId, // bid.bidderRequestId cpm: serverBid.price, @@ -519,6 +563,8 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst, bid dealId: serverBid.dealid }; + bidObject.savedPrebidBid = bidResponse; + if (serverBid.ext && serverBid.ext.bidder && serverBid.ext.bidder.grid && serverBid.ext.bidder.grid.demandSource) { bidResponse.adserverTargeting = { 'hb_ds': serverBid.ext.bidder.grid.demandSource }; bidResponse.meta.demandSource = serverBid.ext.bidder.grid.demandSource; @@ -664,6 +710,13 @@ function reformatKeywords(pageKeywords) { return Object.keys(formatedPageKeywords).length && formatedPageKeywords; } +function canPublishResponse(price, savedPrice) { + if (isNumber(savedPrice)) { + return price > savedPrice || (price === savedPrice && Math.random() > 0.5); + } + return true; +} + function outstreamRender (bid) { bid.renderer.push(() => { window.ANOutstreamVideo.renderAd({ @@ -697,4 +750,369 @@ export function getSyncUrl() { return SYNC_URL; } +// ================ Criteo methods ================== + +const criteoSpec = { + /** f + * @param {object} bid + * @return {boolean} + */ + isBidRequestValid: (bid) => { + // either one of zoneId or networkId should be set + if (!(bid && bid.params && (bid.params.zoneId || bid.params.networkId))) { + return false; + } + + // video media types requires some mandatory params + if (hasVideoMediaType(bid)) { + if (!hasValidVideoMediaType(bid)) { + return false; + } + } + + return true; + }, + + /** + * @param {BidRequest[]} bidRequests + * @param {*} bidderRequest + * @return {ServerRequest} + */ + buildRequests: (bidRequests, bidderRequest) => { + let url; + let data; + let fpd = bidderRequest.ortb2 || {}; + + Object.assign(bidderRequest, { + publisherExt: fpd.site?.ext, + userExt: fpd.user?.ext, + ceh: config.getConfig('criteo.ceh'), + coppa: config.getConfig('coppa') + }); + + const context = buildContext(bidRequests, bidderRequest); + url = buildCdbUrl(context); + data = buildCdbRequest(context, bidRequests, bidderRequest); + + if (data) { + return { method: 'POST', url, data, bidRequests }; + } + }, + + /** + * @param {*} response + * @param {ServerRequest} request + * @return {Bid[]} + */ + interpretResponse: (response, request) => { + const body = response.body || response; + const bids = []; + + if (body && body.slots && isArray(body.slots)) { + body.slots.forEach(slot => { + const bidRequest = find(request.bidRequests, b => b.adUnitCode === slot.impid && (!b.params.zoneId || parseInt(b.params.zoneId) === slot.zoneid)); + const bidId = bidRequest.bidId; + const bid = { + requestId: bidId, + cpm: slot.cpm, + currency: slot.currency, + netRevenue: true, + ttl: slot.ttl || 60, + creativeId: slot.creativecode, + width: slot.width, + height: slot.height, + dealId: slot.dealCode, + }; + if (body.ext?.paf?.transmission && slot.ext?.paf?.content_id) { + const pafResponseMeta = { + content_id: slot.ext.paf.content_id, + transmission: response.ext.paf.transmission + }; + bid.meta = Object.assign({}, bid.meta, { paf: pafResponseMeta }); + } + if (slot.adomain) { + bid.meta = Object.assign({}, bid.meta, { advertiserDomains: slot.adomain }); + } + if (slot.video) { + bid.vastUrl = slot.displayurl; + bid.mediaType = VIDEO; + } else { + bid.ad = slot.creative; + } + bids.push(bid); + }); + } + + return bids; + } +}; + +function readFromAllStorages(name) { + const fromCookie = storage.getCookie(name); + const fromLocalStorage = storage.getDataFromLocalStorage(name); + + return fromCookie || fromLocalStorage || undefined; +} + +/** + * @param {BidRequest[]} bidRequests + * @param bidderRequest + */ +function buildContext(bidRequests, bidderRequest) { + let referrer = ''; + if (bidderRequest && bidderRequest.refererInfo) { + referrer = bidderRequest.refererInfo.page; + } + const queryString = parseUrl(bidderRequest?.refererInfo?.topmostLocation).search; + + const context = { + url: referrer, + debug: queryString['pbt_debug'] === '1', + noLog: queryString['pbt_nolog'] === '1', + amp: false, + }; + + bidRequests.forEach(bidRequest => { + if (bidRequest.params.integrationMode === 'amp') { + context.amp = true; + } + }); + + return context; +} + +/** + * @param {CriteoContext} context + * @return {string} + */ +function buildCdbUrl(context) { + let url = CDB_ENDPOINT; + url += '?profileId=' + PROFILE_ID_INLINE; + url += '&av=' + String(ADAPTER_VERSION_FOR_CRITEO_MODE); + url += '&wv=' + encodeURIComponent('$prebid.version$'); + url += '&cb=' + String(Math.floor(Math.random() * 99999999999)); + + if (storage.localStorageIsEnabled()) { + url += '&lsavail=1'; + } else { + url += '&lsavail=0'; + } + + if (context.amp) { + url += '&im=1'; + } + if (context.debug) { + url += '&debug=1'; + } + if (context.noLog) { + url += '&nolog=1'; + } + + const bundle = readFromAllStorages(BUNDLE_COOKIE_NAME); + if (bundle) { + url += `&bundle=${bundle}`; + } + + const optout = readFromAllStorages(OPTOUT_COOKIE_NAME); + if (optout) { + url += `&optout=1`; + } + + const sid = readFromAllStorages(SID_COOKIE_NAME); + if (sid) { + url += `&sid=${sid}`; + } + + const idcpy = readFromAllStorages(IDCPY_COOKIE_NAME); + if (idcpy) { + url += `&idcpy=${idcpy}`; + } + + return url; +} + +/** + * @param {CriteoContext} context + * @param {BidRequest[]} bidRequests + * @param bidderRequest + * @return {*} + */ +function buildCdbRequest(context, bidRequests, bidderRequest) { + let networkId; + let schain; + const request = { + publisher: { + url: context.url, + ext: bidderRequest.publisherExt, + }, + regs: { + coppa: bidderRequest.coppa === true ? 1 : (bidderRequest.coppa === false ? 0 : undefined) + }, + slots: bidRequests.map(bidRequest => { + networkId = bidRequest.params.networkId || networkId; + schain = bidRequest.schain || schain; + const slot = { + impid: bidRequest.adUnitCode, + transactionid: bidRequest.transactionId, + auctionId: bidRequest.auctionId, + }; + if (bidRequest.params.zoneId) { + slot.zoneid = bidRequest.params.zoneId; + } + if (deepAccess(bidRequest, 'ortb2Imp.ext')) { + slot.ext = bidRequest.ortb2Imp.ext; + } + if (bidRequest.params.ext) { + slot.ext = Object.assign({}, slot.ext, bidRequest.params.ext); + } + if (bidRequest.params.publisherSubId) { + slot.publishersubid = bidRequest.params.publisherSubId; + } + + if (hasBannerMediaType(bidRequest)) { + slot.sizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'), parseSize); + } else { + slot.sizes = []; + } + + if (hasVideoMediaType(bidRequest)) { + const video = { + playersizes: parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize'), parseSize), + mimes: bidRequest.mediaTypes.video.mimes, + protocols: bidRequest.mediaTypes.video.protocols, + maxduration: bidRequest.mediaTypes.video.maxduration, + api: bidRequest.mediaTypes.video.api, + skip: bidRequest.mediaTypes.video.skip, + placement: bidRequest.mediaTypes.video.placement, + minduration: bidRequest.mediaTypes.video.minduration, + playbackmethod: bidRequest.mediaTypes.video.playbackmethod, + startdelay: bidRequest.mediaTypes.video.startdelay + }; + const paramsVideo = bidRequest.params.video; + if (paramsVideo !== undefined) { + video.skip = video.skip || paramsVideo.skip || 0; + video.placement = video.placement || paramsVideo.placement; + video.minduration = video.minduration || paramsVideo.minduration; + video.playbackmethod = video.playbackmethod || paramsVideo.playbackmethod; + video.startdelay = video.startdelay || paramsVideo.startdelay || 0; + } + + slot.video = video; + } + + enrichSlotWithFloors(slot, bidRequest); + + return slot; + }), + }; + if (networkId) { + request.publisher.networkid = networkId; + } + if (schain) { + request.source = { + ext: { + schain: schain + } + }; + } + request.user = { + ext: bidderRequest.userExt + }; + if (bidderRequest && bidderRequest.ceh) { + request.user.ceh = bidderRequest.ceh; + } + if (bidderRequest && bidderRequest.gdprConsent) { + request.gdprConsent = {}; + if (typeof bidderRequest.gdprConsent.gdprApplies !== 'undefined') { + request.gdprConsent.gdprApplies = !!(bidderRequest.gdprConsent.gdprApplies); + } + request.gdprConsent.version = bidderRequest.gdprConsent.apiVersion; + if (typeof bidderRequest.gdprConsent.consentString !== 'undefined') { + request.gdprConsent.consentData = bidderRequest.gdprConsent.consentString; + } + } + if (bidderRequest && bidderRequest.uspConsent) { + request.user.uspIab = bidderRequest.uspConsent; + } + return request; +} + +function parseSizes(sizes, parser = s => s) { + if (sizes === undefined) { + return []; + } + if (Array.isArray(sizes[0])) { // is there several sizes ? (ie. [[728,90],[200,300]]) + return sizes.map(size => parser(size)); + } + return [parser(sizes)]; // or a single one ? (ie. [728,90]) +} + +function parseSize(size) { + return size[0] + 'x' + size[1]; +} + +function hasVideoMediaType(bidRequest) { + return deepAccess(bidRequest, 'mediaTypes.video') !== undefined; +} + +function hasBannerMediaType(bidRequest) { + return deepAccess(bidRequest, 'mediaTypes.banner') !== undefined; +} + +function hasValidVideoMediaType(bidRequest) { + let isValid = true; + + var requiredMediaTypesParams = ['mimes', 'playerSize', 'maxduration', 'protocols', 'api', 'skip', 'placement', 'playbackmethod']; + + requiredMediaTypesParams.forEach(function (param) { + if (deepAccess(bidRequest, 'mediaTypes.video.' + param) === undefined && deepAccess(bidRequest, 'params.video.' + param) === undefined) { + isValid = false; + logError('TheMediaGrid Bid Adapter (withCriteo mode): mediaTypes.video.' + param + ' is required'); + } + }); + + if (isValid) { + const videoPlacement = bidRequest.mediaTypes.video.placement || bidRequest.params.video.placement; + // We do not support long form for now, also we have to check that context & placement are consistent + if (bidRequest.mediaTypes.video.context === 'instream' && videoPlacement === 1) { + return true; + } else if (bidRequest.mediaTypes.video.context === 'outstream' && videoPlacement !== 1) { + return true; + } + } + + return false; +} + +function enrichSlotWithFloors(slot, bidRequest) { + try { + const slotFloors = {}; + + if (bidRequest.getFloor) { + if (bidRequest.mediaTypes?.banner) { + slotFloors.banner = {}; + const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')) + bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({ size: bannerSize, mediaType: BANNER })); + } + + if (bidRequest.mediaTypes?.video) { + slotFloors.video = {}; + const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) + videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({ size: videoSize, mediaType: VIDEO })); + } + + if (Object.keys(slotFloors).length > 0) { + if (!slot.ext) { + slot.ext = {} + } + Object.assign(slot.ext, { + floors: slotFloors + }); + } + } + } catch (e) { + logError('Could not parse floors from Prebid: ' + e); + } +} + registerBidder(spec); diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index f98ca683024..4b93c287fee 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -44,8 +44,9 @@ describe('TheMediaGrid Adapter', function () { return JSON.parse(data); } const bidderRequest = { - refererInfo: {page: 'https://example.com'}, + refererInfo: { page: 'https://example.com' }, bidderRequestId: '22edbae2733bf6', + transactionId: '1239bd74-4511-4335-af21-e828852e25d7', auctionId: '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', timeout: 3000 }; @@ -67,6 +68,7 @@ describe('TheMediaGrid Adapter', function () { 'bidId': '42dbe3a7168a6a', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + transactionId: '1239bd74-4511-4335-af21-e828852e25d7', }, { 'bidder': 'grid', @@ -78,6 +80,7 @@ describe('TheMediaGrid Adapter', function () { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + transactionId: '1239bd74-4511-4335-af21-e828852e25d7', }, { 'bidder': 'grid', @@ -95,6 +98,7 @@ describe('TheMediaGrid Adapter', function () { 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + transactionId: '1239bd74-4511-4335-af21-e828852e25d7', }, { 'bidder': 'grid', @@ -115,6 +119,7 @@ describe('TheMediaGrid Adapter', function () { 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + transactionId: '1239bd74-4511-4335-af21-e828852e25d7', } ]; @@ -395,6 +400,158 @@ describe('TheMediaGrid Adapter', function () { getDataFromLocalStorageStub.restore(); }); + it('should send additional request with adUnits with withCriteo parameter', function () { + const fpdUserIdVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage').callsFake( + arg => arg === 'tmguid' ? fpdUserIdVal : null); + + const bidRequestsWithCriteo = bidRequests.map((bid, i) => + i % 2 ? bid : { + ...bid, + params: { + ...bid.params, + withCriteo: true, + networkId: 123 + i + }, + mediaTypes: { + ...bid.mediaTypes, + ...(bid.mediaTypes.video && { video: { + ...bid.mediaTypes.video, + context: 'instream', + protocols: [1, 2, 3], + maxduration: 30, + api: [1, 2], + skip: 1, + placement: 1, + minduration: 0, + playbackmethod: 1, + startdelay: 0 + } + }) + } + }); + + const [request, criteoRequest] = spec.buildRequests(bidRequestsWithCriteo, bidderRequest); + expect(request.data).to.be.an('string'); + expect(criteoRequest.data).to.be.an('object'); + const payload = parseRequest(request.data); + const criteoPayload = criteoRequest.data; + expect(request.url).to.equal('https://grid.bidswitch.net/hbjson'); + expect(criteoRequest.url.replace(/\?.*$/, '')) + .to.equal('https://bidder.criteo.com/cdb'); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'user': { + 'id': fpdUserIdVal + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[1].bidId, + 'tagid': bidRequests[1].params.uid, + 'ext': {'divid': bidRequests[1].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[2].bidId, + 'tagid': bidRequests[2].params.uid, + 'ext': {'divid': bidRequests[2].adUnitCode}, + 'video': { + 'w': 400, + 'h': 600, + 'protocols': [1, 2, 3], + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'], + } + }, { + 'id': bidRequests[3].bidId, + 'tagid': bidRequests[3].params.uid, + 'ext': {'divid': bidRequests[3].adUnitCode}, + 'banner': { + 'w': 728, + 'h': 90, + 'format': [{'w': 728, 'h': 90}] + }, + 'video': { + 'w': 400, + 'h': 600, + 'protocols': [1, 2, 3] + } + }] + }); + + expect(criteoPayload).to.deep.equal({ + 'publisher': { + 'ext': undefined, + 'url': bidderRequest.refererInfo.page, + 'networkid': 125 + }, + 'regs': { + 'coppa': undefined + }, + 'slots': [{ + 'impid': bidRequests[0].adUnitCode, + 'transactionid': bidderRequest.transactionId, + 'auctionId': bidderRequest.auctionId, + 'sizes': ['300x250', '300x600'] + }, { + 'impid': bidRequests[2].adUnitCode, + 'transactionid': bidderRequest.transactionId, + 'auctionId': bidderRequest.auctionId, + 'sizes': [], + 'video': { + 'api': [ + 1, + 2 + ], + 'maxduration': 30, + 'mimes': [ + 'video/mp4', + 'video/webm', + 'application/javascript', + 'video/ogg', + ], + 'minduration': 0, + 'placement': 1, + 'playbackmethod': 1, + 'playersizes': [ + '400x600' + ], + 'protocols': [ + 1, + 2, + 3 + ], + 'skip': 1, + 'startdelay': 0 + } + }], + 'user': { + 'ext': undefined + } + }); + + getDataFromLocalStorageStub.restore(); + }); + it('if gdprConsent is present payload must have gdpr params', function () { const gdprBidderRequest = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); const [request] = spec.buildRequests(bidRequests, gdprBidderRequest); @@ -1365,6 +1522,137 @@ describe('TheMediaGrid Adapter', function () { expect(result).to.deep.equal(expectedResponse); }); + it('should add response with biggest price', function () { + const mainResponse = [ + {'bid': [{'impid': '2164be6358b9', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, + {'bid': [{'impid': '4e111f1b66e4', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300, dealid: 12}], 'seat': '1'}, + ]; + const criteoResponse = [ + { + impid: 'adunit-code-1', + cpm: 0.15, + creative: '
test content 3
', + creativecode: 1, + bidId: '2164be6358b9', + currency: 'USD', + width: 300, + height: 250, + dealCode: 11, + zoneid: 123, + ttl: 360, + adomain: ['criteo.com'], + }, + { + impid: 'adunit-code-1', + cpm: 1, + creative: '
test content 4
', + creativecode: 2, + bidId: '4e111f1b66e4', + currency: 'USD', + width: 300, + height: 600, + dealCode: 12, + zoneid: 456, + ttl: 360, + adomain: ['criteo.com'], + } + ]; + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1', + 'zoneId': 123, + 'withCriteo': true + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2164be6358b9', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + 'transactionId': '43534f55b213ac', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '2', + 'zoneId': 456, + 'withCriteo': true + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4e111f1b66e4', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + 'transactionId': '43534f55b213ac', + } + ]; + const bidderRequest = { + refererInfo: { page: 'https://example.com' }, + bidderRequestId: '106efe3247', + transactionId: '43534f55b213ac', + auctionId: '32a1f276cb87cb8', + timeout: 3000 + }; + const [mainRequest, criteoRequest] = spec.buildRequests(bidRequests, bidderRequest); + const expectedMainResponse = [ + { + 'requestId': '2164be6358b9', + 'cpm': 1.15, + 'creativeId': 1, + 'dealId': 11, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + 'meta': { + advertiserDomains: [] + }, + }, + { + 'requestId': '4e111f1b66e4', + 'cpm': 0.5, + 'creativeId': 2, + 'dealId': 12, + 'width': 300, + 'height': 600, + 'ad': '
test content 2
', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + 'meta': { + advertiserDomains: [] + }, + } + ]; + const expectedCriteoResponse = [ + { + 'requestId': '4e111f1b66e4', + 'cpm': 1, + 'creativeId': 2, + 'dealId': 12, + 'width': 300, + 'height': 600, + 'ad': '
test content 4
', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + 'meta': { + advertiserDomains: ['criteo.com'] + }, + } + ]; + + const mainResult = spec.interpretResponse({'body': {'seatbid': mainResponse}}, mainRequest); + const criteoResult = spec.interpretResponse({'body': {'slots': criteoResponse}}, criteoRequest); + expect(mainResult).to.deep.equal(expectedMainResponse); + expect(criteoResult).to.deep.equal(expectedCriteoResponse); + }); + it('response with ext.bidder.grid.demandSource', function () { const bidRequests = [ { From a88c296ae95fe7c1f9a32a7fc8a23e718ba4d306 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Thu, 17 Nov 2022 14:59:49 +0100 Subject: [PATCH 450/569] Tappx Bid Adapter: getting correct site page (#9187) * Fix: creating host correctly when http or https are added from the beginning * Fix :: Changed double quotes for single quotes * Fix :: Getting the full page URL * Fix :: Changed order params * Fix :: Replaced quotes from double to simple * Fix :: Adapting format to lint * Remove TODO comment * Added more controls * camelcase fix * Changed test * Remove "inIframe" util Co-authored-by: Jordi Arnau Co-authored-by: ruben_tappx --- modules/tappxBidAdapter.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 11aa6c76c76..c7a54431ee9 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -11,7 +11,7 @@ const BIDDER_CODE = 'tappx'; const GVLID_CODE = 628; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.1005'; +const TAPPX_BIDDER_VERSION = '0.1.2'; const TYPE_CNN = 'prebidjs'; const LOG_PREFIX = '[TAPPX]: '; const VIDEO_SUPPORT = ['instream', 'outstream']; @@ -271,12 +271,28 @@ function buildOneRequest(validBidRequests, bidderRequest) { api[0] = deepAccess(validBidRequests, 'params.api') ? deepAccess(validBidRequests, 'params.api') : [3, 5]; } else { let bundle = _extractPageUrl(validBidRequests, bidderRequest); - let site = {}; + let site = deepAccess(validBidRequests, 'params.site') || {}; site.name = bundle; + site.page = bidderRequest?.refererInfo?.page || deepAccess(validBidRequests, 'params.site.page') || bidderRequest?.refererInfo?.topmostLocation || window.location.href || bundle; site.domain = bundle; + site.ref = bidderRequest?.refererInfo?.ref || window.top.document.referrer || ''; + site.ext = {}; + site.ext.is_amp = bidderRequest?.refererInfo?.isAmp || 0; + site.ext.page_da = deepAccess(validBidRequests, 'params.site.page') || '-'; + site.ext.page_rip = bidderRequest?.refererInfo?.page || '-'; + site.ext.page_rit = bidderRequest?.refererInfo?.topmostLocation || '-'; + site.ext.page_wlh = window.location.href || '-'; publisher.name = bundle; publisher.domain = bundle; + let sitename = document.getElementsByTagName('meta')['title']; + if (sitename && sitename.content) { + site.name = sitename.content; + } tagid = `${site.name}_typeAdBanVid_${getOs()}`; + let keywords = document.getElementsByTagName('meta')['keywords']; + if (keywords && keywords.content) { + site.keywords = keywords.content; + } payload.site = site; } // < App/Site object @@ -295,9 +311,9 @@ function buildOneRequest(validBidRequests, bidderRequest) { if ( ((bannerMediaType.sizes[0].indexOf(480) >= 0) && (bannerMediaType.sizes[0].indexOf(320) >= 0)) || ((bannerMediaType.sizes[0].indexOf(768) >= 0) && (bannerMediaType.sizes[0].indexOf(1024) >= 0))) { - banner.pos = 7; + banner.pos = 0; } else { - banner.pos = 4; + banner.pos = 0; } banner.api = api; @@ -565,8 +581,7 @@ export function _checkParamDataType(key, value, datatype) { } export function _extractPageUrl(validBidRequests, bidderRequest) { - // TODO: does the fallback make sense? - let url = bidderRequest?.refererInfo?.page || bidderRequest.refererInfo?.topmostLocation; + let url = bidderRequest?.refererInfo?.page || bidderRequest?.refererInfo?.topmostLocation; return parseDomain(url, {noLeadingWww: true}); } From d0c1d741bd4828c8d6d6f8170a9b4c834d64e70c Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 17 Nov 2022 15:44:44 +0000 Subject: [PATCH 451/569] Prebid 7.26.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 462484cb962..a2bf0986f9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.26.0-pre", + "version": "7.26.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 8df1268bbd2..d86a2cccd1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.26.0-pre", + "version": "7.26.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From e680dd34486d7f1ffec6f52fe4599537a62a12a6 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 17 Nov 2022 15:44:45 +0000 Subject: [PATCH 452/569] Increment version to 7.27.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2bf0986f9d..0a3b01b972f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.26.0", + "version": "7.27.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index d86a2cccd1d..d243b650b6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.26.0", + "version": "7.27.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 974e21db74dadb6cb082b2758d77f6af614c91f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 10:47:25 -0700 Subject: [PATCH 453/569] Bump loader-utils from 2.0.3 to 2.0.4 (#9256) Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.3 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.3...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a3b01b972f..4584231d42c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "7.24.0-pre", + "version": "7.27.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -16304,9 +16304,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", - "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -25260,6 +25260,7 @@ } }, "plugins/eslint": { + "name": "eslint-plugin-prebid", "version": "1.0.0", "dev": true, "license": "Apache-2.0" @@ -37926,9 +37927,9 @@ "dev": true }, "loader-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", - "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "requires": { "big.js": "^5.2.2", From 2d46b828d37e4ab063fcb7f965188da84aed45ad Mon Sep 17 00:00:00 2001 From: Tobias von Klipstein Date: Fri, 18 Nov 2022 15:14:40 +0100 Subject: [PATCH 454/569] glomex Bidder: expose glomex GVL id (#9262) --- modules/glomexBidAdapter.js | 2 ++ test/spec/modules/glomexBidAdapter_spec.js | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/modules/glomexBidAdapter.js b/modules/glomexBidAdapter.js index 32c2036a748..5c9b3c1fa28 100644 --- a/modules/glomexBidAdapter.js +++ b/modules/glomexBidAdapter.js @@ -4,9 +4,11 @@ import {BANNER} from '../src/mediaTypes.js'; const ENDPOINT = 'https://prebid.mes.glomex.cloud/request-bid' const BIDDER_CODE = 'glomex' +const GVLID = 967 export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { diff --git a/test/spec/modules/glomexBidAdapter_spec.js b/test/spec/modules/glomexBidAdapter_spec.js index 30157da858b..8c53db8d605 100644 --- a/test/spec/modules/glomexBidAdapter_spec.js +++ b/test/spec/modules/glomexBidAdapter_spec.js @@ -49,6 +49,10 @@ const RESPONSE = { describe('glomexBidAdapter', function () { const adapter = newBidder(spec) + it('should expose gvlid', function() { + expect(spec.gvlid).to.equal(967) + }); + describe('inherited functions', function () { it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function') From e1661af611eb82543faedbbf4d3b95475a36191e Mon Sep 17 00:00:00 2001 From: nkloeber <100145701+nkloeber@users.noreply.github.com> Date: Fri, 18 Nov 2022 17:42:37 +0100 Subject: [PATCH 455/569] Update Floor format to floor={adslotId}:{floorPriceInCents}[, ...] and fix the size which is submitted to the Price Floors Module (#9186) --- modules/yieldlabBidAdapter.js | 14 ++-- test/spec/modules/yieldlabBidAdapter_spec.js | 69 ++++++++++++++------ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index e6758e78022..88e1494d416 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -41,6 +41,7 @@ export const spec = { const adslotIds = [] const adslotSizes = []; + const adslotFloors = []; const timestamp = Date.now() const query = { ts: timestamp, @@ -77,7 +78,7 @@ export const spec = { } const floor = getBidFloor(bid, sizes) if (floor) { - query.floor = floor; + adslotFloors.push(bid.params.adslotId + ':' + floor); } }) @@ -99,6 +100,11 @@ export const spec = { if (adslotSizes.length > 0) { query.sizes = adslotSizes.join(',') } + + if (adslotFloors.length > 0) { + query.floor = adslotFloors.join(',') + } + const queryString = createQueryString(query) return { @@ -463,7 +469,7 @@ function extractSizes(bid) { * * @param {Object} bid * @param {string[]} sizes - * @returns The floor CPM of a matched rule based on the rule selection process (mediaType, size and currency), + * @returns The floor CPM in cents of a matched rule based on the rule selection process (mediaType, size and currency), * using the getFloor() inputs. Multi sizes and unsupported media types will default to '*' */ function getBidFloor(bid, sizes) { @@ -475,10 +481,10 @@ function getBidFloor(bid, sizes) { const floor = bid.getFloor({ currency: CURRENCY_CODE, mediaType: mediaType !== undefined && spec.supportedMediaTypes.includes(mediaType) ? mediaType : '*', - size: sizes.length !== 1 ? '*' : extractSizes(sizes) + size: sizes.length !== 1 ? '*' : sizes[0].split(DIMENSION_SIGN) }); if (floor.currency === CURRENCY_CODE) { - return floor.floor; + return (floor.floor * 100).toFixed(0); } return undefined; } diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 9155884106c..5df0e93d34e 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -658,36 +658,65 @@ describe('yieldlabBidAdapter', () => { }); describe('getBidFloor', function () { - let bidRequest, getFloor; - - it('should add valid bid floor', () => { - getFloor = () => { - return { - currency: 'EUR', - floor: 1.33 - }; - }; + let bidRequest, bidRequest2, currency, floor; + const getFloor = () => { + return { + currency: currency, + floor: floor + } + } + + it('should add valid bid floor in the format floor={adslotId}:{floorPriceInCents}[, ...]', () => { + bidRequest = Object.assign(DEFAULT_REQUEST(), { + getFloor: () => { + return { + currency: 'EUR', + floor: 1.33 + } + }}); + bidRequest2 = Object.assign(DEFAULT_REQUEST(), { + params: { + adslotId: 2222 + }, + getFloor: () => { + return { + currency: 'EUR', + floor: 2.99 + } + } + }); + const result = spec.buildRequests([bidRequest, bidRequest2], REQPARAMS) + expect(result).to.have.nested.property('queryParams.floor', '1111:133,2222:299') + }); + + it('should round the floor price up', () => { + currency = 'EUR'; + floor = 0.745; bidRequest = Object.assign(DEFAULT_REQUEST(), {getFloor}) const result = spec.buildRequests([bidRequest], REQPARAMS) - expect(result).to.have.nested.property('queryParams.floor', 1.33) + expect(result).to.have.nested.property('queryParams.floor', '1111:75') }); - it('should not add empty bid floor', () => { - getFloor = () => { - return {}; - }; + it('should round the floor price down', () => { + currency = 'EUR'; + floor = 0.034; bidRequest = Object.assign(DEFAULT_REQUEST(), {getFloor}) const result = spec.buildRequests([bidRequest], REQPARAMS) + expect(result).to.have.nested.property('queryParams.floor', '1111:3') + }); + + it('should not add empty bid floor', () => { + bidRequest = Object.assign(DEFAULT_REQUEST(), { + getFloor: () => { + return {}; + }}) + const result = spec.buildRequests([bidRequest], REQPARAMS) expect(result).not.to.have.nested.property('queryParams.floor') }); it('should not add bid floor when currency is not matching', () => { - getFloor = (currency, mediaType, size) => { - return { - currency: 'USD', - floor: 1.33 - }; - }; + currency = 'USD'; + floor = 1.33; bidRequest = Object.assign(DEFAULT_REQUEST(), {getFloor}) const result = spec.buildRequests([bidRequest], REQPARAMS) expect(result).not.to.have.nested.property('queryParams.floor') From a1132c103e09ac6192e80b05d8786a715c0536e5 Mon Sep 17 00:00:00 2001 From: rahulgravito <105201950+rahulgravito@users.noreply.github.com> Date: Fri, 18 Nov 2022 23:01:06 +0530 Subject: [PATCH 456/569] Gravito Id System : variable update to fix tests (#9259) * gravitompId user module for integrating gravito first party cookie with prebid js * fixed eslint issues raised by circleci * fixed trailing spaces error raised by circleci * Merge branch 'master' of https://github.com/GravitoLtd/Prebid.js * minor changes to GravitoIdSystem user module to make sure that Gravito Id is visible in bid request. * changes to Gravito User module to fix issue with Gravito Id not appearing in bid request. * fixing issue with Gravito Id not appearing in bid request. * modified test case to adapt changes made to the GravitoIdSystem user module * change to spec file to fix for test failure * changes to GravitoIdSystem spec file. * Fix for trailing space issue in Circle CI --- modules/gravitoIdSystem.js | 7 +++---- test/spec/modules/gravitoIdSystem_spec.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/gravitoIdSystem.js b/modules/gravitoIdSystem.js index 30fa3abd6d2..809263a1c68 100644 --- a/modules/gravitoIdSystem.js +++ b/modules/gravitoIdSystem.js @@ -39,19 +39,18 @@ export const gravitoIdSystemSubmodule = { * decode the stored id value for passing to bid requests * @function * @param { {gravitompId: string} } value - * @returns { {gravitompId: {id: string} } | undefined } + * @returns { {gravitompId: {string} } | undefined } */ decode: function(value) { if (value && typeof value === 'object') { - const result = {}; + var result = {}; if (value.gravitompId) { - result.id = value.gravitompId + result = value.gravitompId } return {gravitompId: result}; } return undefined; }, - } submodule('userId', gravitoIdSystemSubmodule); diff --git a/test/spec/modules/gravitoIdSystem_spec.js b/test/spec/modules/gravitoIdSystem_spec.js index e904355f7f1..9584f60c81d 100644 --- a/test/spec/modules/gravitoIdSystem_spec.js +++ b/test/spec/modules/gravitoIdSystem_spec.js @@ -40,7 +40,7 @@ describe('gravitompId module', function () { describe('decode()', function () { it('should return the gravitompId when it exists in cookie', function () { const decoded = gravitoIdSystemSubmodule.decode(GRAVITOID_TEST_OBJ); - expect(decoded).to.be.deep.equal({gravitompId: {id: GRAVITOID_TEST_VALUE}}); + expect(decoded).to.be.deep.equal({gravitompId: GRAVITOID_TEST_VALUE}); }); it('should return the undefined when decode id is not "string"', function () { From 1a5f5ffd04adc422d443d643f3f70bc95b4382df Mon Sep 17 00:00:00 2001 From: Fatih Kaya Date: Mon, 21 Nov 2022 18:52:59 +0300 Subject: [PATCH 457/569] AdMatic Bidder: added User-Snyc url for alias (#9261) * Admatic Bidder Adaptor * Update admaticBidAdapter.md * Update admaticBidAdapter.md * remove floor parameter * Update admaticBidAdapter.js * Admatic Bid Adapter: alias and bid floor features activated * Admatic adapter: host param control changed * Alias name changed. * Revert "Admatic adapter: host param control changed" This reverts commit de7ac85981b1ba3ad8c5d1dc95c5dadbdf5b9895. * added alias feature and host param * Revert "added alias feature and host param" This reverts commit 6ec8f4539ea6be403a0d7e08dad5c7a5228f28a1. * Revert "Alias name changed." This reverts commit 661c54f9b2397e8f25c257144d73161e13466281. * Revert "Admatic Bid Adapter: alias and bid floor features activated" This reverts commit 7a2e0e29c49e2f876b68aafe886b336fe2fe6fcb. * Revert "Update admaticBidAdapter.js" This reverts commit 7a845b7151bbb08addfb58ea9bd5b44167cc8a4e. * Revert "remove floor parameter" This reverts commit 7a23b055ccd4ea23d23e73248e82b21bc6f69d90. * Admatic adapter: host param control && Add new Bidder * Revert "Admatic adapter: host param control && Add new Bidder" This reverts commit 3c797b120c8e0fe2b851381300ac5c4b1f92c6e2. * commit new features * Update admaticBidAdapter.js * updated for coverage * sync updated * Update adloader.js * AdMatic Bidder: development of user sync url * Update admaticBidAdapter.js --- modules/admaticBidAdapter.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js index ec4401b77c9..148424c0a98 100644 --- a/modules/admaticBidAdapter.js +++ b/modules/admaticBidAdapter.js @@ -1,7 +1,7 @@ import { getValue, logError, deepAccess, getBidIdParameter, isArray } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -const SYNC_URL = 'https://cdn.serve.admatic.com.tr/showad/sync.html'; +let SYNC_URL = ''; const BIDDER_CODE = 'admatic'; export const spec = { code: BIDDER_CODE, @@ -59,6 +59,15 @@ export const spec = { }; if (payload) { + switch (bidderName) { + case 'pixad': + SYNC_URL = 'https://static.pixad.com.tr/sync.html'; + break; + default: + SYNC_URL = 'https://cdn.serve.admatic.com.tr/showad/sync.html'; + break; + } + return { method: 'POST', url: `https://${host}/pb?bidder=${bidderName}`, data: payload, options: { contentType: 'application/json' } }; } }, From f5fdcf095ee1be64b437a2ac3e4191af2aa3d44b Mon Sep 17 00:00:00 2001 From: Omer Dotan <54346241+omerBrowsi@users.noreply.github.com> Date: Mon, 21 Nov 2022 20:03:57 +0200 Subject: [PATCH 458/569] Browsi RTD Module: add pageview billable event (#9207) * real time data module, browsi sub module for real time data, new hook bidsBackCallback, fix for config unsubscribe * change timeout&primary ad server only to auctionDelay update docs * support multiple providers * change promise to callbacks configure submodule on submodules.json * bug fixes * use Prebid ajax * tests fix * browsi real time data provider improvements * real time data module, browsi sub module for real time data, new hook bidsBackCallback, fix for config unsubscribe * change timeout&primary ad server only to auctionDelay update docs * support multiple providers * change promise to callbacks configure submodule on submodules.json * bug fixes * use Prebid ajax * tests fix * browsi real time data provider improvements * fire billable event according to event listener --- modules/browsiRtdProvider.js | 10 ++++++---- test/spec/modules/browsiRtdProvider_spec.js | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 0996fc9905b..31b2d709f35 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -59,10 +59,12 @@ export function addBrowsiTag(data) { export function sendPageviewEvent(eventType) { if (eventType === 'PAGEVIEW') { - events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { - vendor: 'browsi', - type: 'pageview', - billingId: generateUUID() + window.addEventListener('browsi_pageview', () => { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: 'browsi', + type: 'pageview', + billingId: generateUUID() + }) }) } } diff --git a/test/spec/modules/browsiRtdProvider_spec.js b/test/spec/modules/browsiRtdProvider_spec.js index 19483047827..75120aa7505 100644 --- a/test/spec/modules/browsiRtdProvider_spec.js +++ b/test/spec/modules/browsiRtdProvider_spec.js @@ -252,7 +252,8 @@ describe('browsi Real time data sub module', function () { }) it('should send event if type is correct', function () { sendPageviewEvent('PAGEVIEW') - + const pageViewEvent = new CustomEvent('browsi_pageview', {}); + window.dispatchEvent(pageViewEvent); const expectedCall = { vendor: 'browsi', type: 'pageview', From d6418a04f9913ee732114f4926f490094f765c9f Mon Sep 17 00:00:00 2001 From: Piotr Jaworski <109736938+piotrj-rtbh@users.noreply.github.com> Date: Mon, 21 Nov 2022 21:10:19 +0100 Subject: [PATCH 459/569] RTB House Bid Adapter: Process FLEDGE request/response (#9215) * RTBHouse Bid Adapter: add global vendor list id * structured user agent - browsers.brands * fix lint errors * Added sda into rtbhouse adapter * spreading ortb2: user & site props * examples reverted * init version * using mergedeep * removed wrong imp array augm.; slot imp augm. with addtl check * [SUA] merging ortb2.device into request * fledge auctionConfig adapted to our bid response structure * new bidder response structure for fledge * make sure bidderRequest has proper flag turned on * fledge endpoint hardcoded; code cleanups * remove obsolete function * obsolete function removed * [RTB House] Process FLEDGE request/response (#4) * [SDA & SUA] refactor using mergedeep * [FLEDGE] fledge auctionConfig adapted to our bid response structure * [FLEDGE] new bidder response structure for fledge * [FLEDGE] make sure bidderRequest has proper flag turned on * [FLEDGE] fledge endpoint hardcoded; code cleanups * [FLEDGE] remove obsolete functions * fixed lint errors * fledge test suites; adapter: delete imp.ext.ae when no fledge (#5) Co-authored-by: Leandro Otani Co-authored-by: rtbh-lotani <83652735+rtbh-lotani@users.noreply.github.com> Co-authored-by: Tomasz Swirski --- modules/rtbhouseBidAdapter.js | 119 ++++++++++++++----- test/spec/modules/rtbhouseBidAdapter_spec.js | 79 ++++++++++++ 2 files changed, 169 insertions(+), 29 deletions(-) diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index fdf64483da7..9f498014a8e 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -1,13 +1,15 @@ -import {deepAccess, isArray, logError} from '../src/utils.js'; +import {deepAccess, mergeDeep, isArray, logError, logInfo} from '../src/utils.js'; import { getOrigin } from '../libraries/getOrigin/index.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {includes} from '../src/polyfill.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'rtbhouse'; const REGIONS = ['prebid-eu', 'prebid-us', 'prebid-asia']; const ENDPOINT_URL = 'creativecdn.com/bidder/prebid/bids'; +const FLEDGE_ENDPOINT_URL = 'creativecdn.com/bidder/prebidfledge/bids'; const DEFAULT_CURRENCY_ARR = ['USD']; // NOTE - USD is the only supported currency right now; Hardcoded for bids const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE]; const TTL = 55; @@ -50,12 +52,13 @@ export const spec = { const request = { id: validBidRequests[0].auctionId, - imp: validBidRequests.map(slot => mapImpression(slot)), + imp: validBidRequests.map(slot => mapImpression(slot, bidderRequest)), site: mapSite(validBidRequests, bidderRequest), cur: DEFAULT_CURRENCY_ARR, test: validBidRequests[0].params.test || 0, source: mapSource(validBidRequests[0]), }; + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { const consentStr = (bidderRequest.gdprConsent.consentString) ? bidderRequest.gdprConsent.consentString.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') : ''; @@ -81,37 +84,32 @@ export const spec = { } } - const ortb2Params = bidderRequest && bidderRequest.ortb2; - if (ortb2Params?.user) { - request.user = { - ...request.user, - ...(ortb2Params.user.data && { - data: { ...request.user?.data, ...ortb2Params.user.data }, - }), - ...(ortb2Params.user.ext && { - ext: { ...request.user?.ext, ...ortb2Params.user.ext }, - }), - }; + const ortb2Params = bidderRequest?.ortb2 || {}; + if (ortb2Params.site) { + mergeDeep(request, { site: ortb2Params.site }); + } + if (ortb2Params.user) { + mergeDeep(request, { user: ortb2Params.user }); + } + if (ortb2Params.device) { + mergeDeep(request, { device: ortb2Params.device }); } - if (ortb2Params?.site) { - request.site = { - ...request.site, - ...(ortb2Params.site.content && { - content: { ...request.site?.content, ...ortb2Params.site.content }, - }), - ...(ortb2Params.site.ext && { - ext: { ...request.site?.ext, ...ortb2Params.site.ext }, - }), - }; + + let computedEndpointUrl = ENDPOINT_URL; + + const fledgeConfig = config.getConfig('fledgeConfig'); + if (bidderRequest.fledgeEnabled && fledgeConfig) { + mergeDeep(request, { ext: { fledge_config: fledgeConfig } }); + computedEndpointUrl = FLEDGE_ENDPOINT_URL; } return { method: 'POST', - url: 'https://' + validBidRequests[0].params.region + '.' + ENDPOINT_URL, + url: 'https://' + validBidRequests[0].params.region + '.' + computedEndpointUrl, data: JSON.stringify(request) }; }, - interpretResponse: function (serverResponse, originalRequest) { + interpretOrtbResponse: function (serverResponse, originalRequest) { const responseBody = serverResponse.body; if (!isArray(responseBody)) { return []; @@ -119,17 +117,72 @@ export const spec = { const bids = []; responseBody.forEach(serverBid => { - if (serverBid.price === 0) { + if (!serverBid.price) { // price may exist and is === 0 or there's no price prop at all (fledge req case) return; } + + let interpretedBid; + // try...catch would be risky cause JSON.parse throws SyntaxError if (serverBid.adm.indexOf('{') === 0) { - bids.push(interpretNativeBid(serverBid)); + interpretedBid = interpretNativeBid(serverBid); } else { - bids.push(interpretBannerBid(serverBid)); + interpretedBid = interpretBannerBid(serverBid); } + if (serverBid.ext) interpretedBid.ext = serverBid.ext; + + bids.push(interpretedBid); }); return bids; + }, + interpretResponse: function (serverResponse, originalRequest) { + let bids; + + const responseBody = serverResponse.body; + let fledgeAuctionConfigs = null; + + if (responseBody.bidid && isArray(responseBody?.ext?.igbid)) { + // we have fledge response + // mimic the original response ([{},...]) + bids = this.interpretOrtbResponse({ body: responseBody.seatbid[0]?.bid }, originalRequest); + + const seller = responseBody.ext.seller; + const decisionLogicUrl = responseBody.ext.decisionLogicUrl; + const sellerTimeout = 'sellerTimeout' in responseBody.ext ? { sellerTimeout: responseBody.ext.sellerTimeout } : {}; + responseBody.ext.igbid.forEach((igbid) => { + const perBuyerSignals = {}; + igbid.igbuyer.forEach(buyerItem => { + perBuyerSignals[buyerItem.igdomain] = buyerItem.buyersignal + }); + fledgeAuctionConfigs = fledgeAuctionConfigs || {}; + fledgeAuctionConfigs[igbid.impid] = mergeDeep( + { + seller, + decisionLogicUrl, + interestGroupBuyers: Object.keys(perBuyerSignals), + perBuyerSignals, + }, + sellerTimeout + ); + }); + } else { + bids = this.interpretOrtbResponse(serverResponse, originalRequest); + } + + if (fledgeAuctionConfigs) { + fledgeAuctionConfigs = Object.entries(fledgeAuctionConfigs).map(([bidId, cfg]) => { + return Object.assign({ + bidId, + auctionSignals: {} + }, cfg); + }); + logInfo('Response with FLEDGE:', { bids, fledgeAuctionConfigs }); + return { + bids, + fledgeAuctionConfigs, + } + } + return bids; } }; registerBidder(spec); @@ -154,7 +207,7 @@ function applyFloor(slot) { * @param {object} slot Ad Unit Params by Prebid * @returns {object} Imp by OpenRTB 2.5 §3.2.4 */ -function mapImpression(slot) { +function mapImpression(slot, bidderRequest) { const imp = { id: slot.bidId, banner: mapBanner(slot), @@ -167,6 +220,14 @@ function mapImpression(slot) { imp.bidfloor = bidfloor; } + if (bidderRequest.fledgeEnabled) { + imp.ext = imp.ext || {}; + imp.ext.ae = slot?.ortb2Imp?.ext?.ae + } else { + if (imp.ext?.ae) { + delete imp.ext.ae; + } + } return imp; } diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js index f4bcb48474a..6d41df7605b 100644 --- a/test/spec/modules/rtbhouseBidAdapter_spec.js +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { OPENRTB, spec } from 'modules/rtbhouseBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; describe('RTBHouseAdapter', () => { const adapter = newBidder(spec); @@ -97,6 +98,10 @@ describe('RTBHouseAdapter', () => { ]; }); + afterEach(function () { + config.resetConfig(); + }); + it('should build test param into the request', () => { let builtTestRequest = spec.buildRequests(bidRequests, bidderRequest).data; expect(JSON.parse(builtTestRequest).test).to.equal(1); @@ -263,6 +268,45 @@ describe('RTBHouseAdapter', () => { expect(data.source).to.not.have.property('ext'); }); + context('FLEDGE', function() { + afterEach(function () { + config.resetConfig(); + }); + + it('sends bid request to FLEDGE ENDPOINT via POST', function () { + let bidRequest = Object.assign([], bidRequests); + delete bidRequest[0].params.test; + config.setConfig({ fledgeConfig: true }); + const request = spec.buildRequests(bidRequest, { ...bidderRequest, fledgeEnabled: true }); + expect(request.url).to.equal('https://prebid-eu.creativecdn.com/bidder/prebidfledge/bids'); + expect(request.method).to.equal('POST'); + }); + + it('when FLEDGE is disabled, should not send imp.ext.ae', function () { + let bidRequest = Object.assign([], bidRequests); + delete bidRequest[0].params.test; + bidRequest[0].ortb2Imp = { + ext: { ae: 2 } + }; + const request = spec.buildRequests(bidRequest, { ...bidderRequest, fledgeEnabled: false }); + let data = JSON.parse(request.data); + if (data.imp[0].ext) { + expect(data.imp[0].ext).to.not.have.property('ae'); + } + }); + + it('when FLEDGE is enabled, should send whatever is set in ortb2imp.ext.ae in all bid requests', function () { + let bidRequest = Object.assign([], bidRequests); + delete bidRequest[0].params.test; + bidRequest[0].ortb2Imp = { + ext: { ae: 2 } + }; + const request = spec.buildRequests(bidRequest, { ...bidderRequest, fledgeEnabled: true }); + let data = JSON.parse(request.data); + expect(data.imp[0].ext.ae).to.equal(2); + }); + }); + describe('native imp', () => { function basicRequest(extension) { return Object.assign({ @@ -460,6 +504,29 @@ describe('RTBHouseAdapter', () => { 'h': 250 }]; + let fledgeResponse = { + 'id': 'bid-identifier', + 'ext': { + 'igbid': [{ + 'impid': 'test-bid-id', + 'igbuyer': [{ + 'igdomain': 'https://buyer-domain.com', + 'buyersignal': {} + }] + }], + 'sellerTimeout': 500, + 'seller': 'https://seller-domain.com', + 'decisionLogicUrl': 'https://seller-domain.com/decision-logic.js' + }, + 'bidid': 'bid-identifier', + 'seatbid': [{ + 'bid': [{ + 'id': 'bid-response-id', + 'impid': 'test-bid-id' + }] + }] + }; + it('should get correct bid response', function () { let expectedResponse = [ { @@ -488,6 +555,18 @@ describe('RTBHouseAdapter', () => { expect(result.length).to.equal(0); }); + context('when the response contains FLEDGE interest groups config', function () { + let bidderRequest; + let response = spec.interpretResponse({body: fledgeResponse}, {bidderRequest}); + + it('should return FLEDGE auction_configs alongside bids', function () { + expect(response).to.have.property('bids'); + expect(response).to.have.property('fledgeAuctionConfigs'); + expect(response.fledgeAuctionConfigs.length).to.equal(1); + expect(response.fledgeAuctionConfigs[0].bidId).to.equal('test-bid-id'); + }); + }); + describe('native', () => { const adm = { native: { From 3d1174bca579f6e01e7187cb82c9d91fff0cc7ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 04:35:05 -0700 Subject: [PATCH 460/569] Bump engine.io from 6.2.0 to 6.2.1 (#9270) Bumps [engine.io](https://github.com/socketio/engine.io) from 6.2.0 to 6.2.1. - [Release notes](https://github.com/socketio/engine.io/releases) - [Changelog](https://github.com/socketio/engine.io/blob/main/CHANGELOG.md) - [Commits](https://github.com/socketio/engine.io/compare/6.2.0...6.2.1) --- updated-dependencies: - dependency-name: engine.io dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4584231d42c..2ed7cf9067e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8885,9 +8885,9 @@ } }, "node_modules/engine.io": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", - "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -32089,9 +32089,9 @@ } }, "engine.io": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", - "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", "dev": true, "requires": { "@types/cookie": "^0.4.1", From 616d957bacc18d4764a82061cf5e44696d62bc8b Mon Sep 17 00:00:00 2001 From: lasloche <62240785+lasloche@users.noreply.github.com> Date: Tue, 22 Nov 2022 13:43:17 +0200 Subject: [PATCH 461/569] Rise readme maintenance (#9272) --- modules/minutemediaBidAdapter.md | 6 +++--- modules/riseBidAdapter.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/minutemediaBidAdapter.md b/modules/minutemediaBidAdapter.md index 70f106a745f..66b54adaf0e 100644 --- a/modules/minutemediaBidAdapter.md +++ b/modules/minutemediaBidAdapter.md @@ -20,7 +20,7 @@ The adapter supports Video(instream) & Banner. | Name | Scope | Type | Description | Example | ---- | ----- | ---- | ----------- | ------- -| `org` | required | String | MinuteMedia publisher Id provided by your MinuteMedia representative | "56f91cd4d3e3660002000033" +| `org` | required | String | MinuteMedia publisher Id provided by your MinuteMedia representative | "1234567890abcdef12345678" | `floorPrice` | optional | Number | Minimum price in USD. Misuse of this parameter can impact revenue | 2.00 | `placementId` | optional | String | A unique placement identifier | "12345678" | `testMode` | optional | Boolean | This activates the test mode | false @@ -43,7 +43,7 @@ var adUnits = [{ bids: [{ bidder: 'minutemedia', params: { - org: '56f91cd4d3e3660002000033', // Required + org: '1234567890abcdef12345678', // Required floorPrice: 2.00, // Optional placementId: 'video-test', // Optional testMode: false // Optional @@ -65,7 +65,7 @@ var adUnits = [{ bids: [{ bidder: 'minutemedia', params: { - org: '56f91cd4d3e3660002000033', // Required + org: '1234567890abcdef12345678', // Required floorPrice: 2.00, // Optional placementId: 'banner-test', // Optional testMode: false // Optional diff --git a/modules/riseBidAdapter.md b/modules/riseBidAdapter.md index 2920fd28528..9dab3ec2a30 100644 --- a/modules/riseBidAdapter.md +++ b/modules/riseBidAdapter.md @@ -20,7 +20,7 @@ The adapter supports Video(instream). | Name | Scope | Type | Description | Example | ---- | ----- | ---- | ----------- | ------- -| `org` | required | String | Rise publisher Id provided by your Rise representative | "56f91cd4d3e3660002000033" +| `org` | required | String | Rise publisher Id provided by your Rise representative | "1234567890abcdef12345678" | `floorPrice` | optional | Number | Minimum price in USD. Misuse of this parameter can impact revenue | 2.00 | `placementId` | optional | String | A unique placement identifier | "12345678" | `testMode` | optional | Boolean | This activates the test mode | false @@ -41,10 +41,10 @@ var adUnits = [ bids: [{ bidder: 'rise', params: { - org: '56f91cd4d3e3660002000033', // Required + org: '1234567890abcdef12345678', // Required floorPrice: 2.00, // Optional placementId: '12345678', // Optional - testMode: false // Optional, + testMode: false, // Optional, rtbDomain: "www.test.com" //Optional } }] From 3e5f08635b965cfbc5384b9735f0797757cedad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9onard=20Labat?= Date: Tue, 22 Nov 2022 18:03:07 +0100 Subject: [PATCH 462/569] Criteo Bid Adapter : fix getFloor usage issue (#9243) getFloor call context is loss when we reference it as a callback ; we need to make sure that it is properly mapped as getFloor function assumes that "this" is the actual bid request object --- modules/criteoBidAdapter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index c0251ab0165..5567103db69 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -672,18 +672,18 @@ function enrichSlotWithFloors(slot, bidRequest) { if (bidRequest.mediaTypes?.banner) { slotFloors.banner = {}; const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')) - bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = getFloor({ size: bannerSize, mediaType: BANNER })); + bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = getFloor.call(bidRequest, { size: bannerSize, mediaType: BANNER })); } if (bidRequest.mediaTypes?.video) { slotFloors.video = {}; const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) - videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = getFloor({ size: videoSize, mediaType: VIDEO })); + videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = getFloor.call(bidRequest, { size: videoSize, mediaType: VIDEO })); } if (bidRequest.mediaTypes?.native) { slotFloors.native = {}; - slotFloors.native['*'] = getFloor({ size: '*', mediaType: NATIVE }); + slotFloors.native['*'] = getFloor.call(bidRequest, { size: '*', mediaType: NATIVE }); } if (Object.keys(slotFloors).length > 0) { From 690c0d63f7455475cd8d0fc773dbdfa7f40fa7d1 Mon Sep 17 00:00:00 2001 From: OronW <41260031+OronW@users.noreply.github.com> Date: Tue, 22 Nov 2022 20:17:24 +0200 Subject: [PATCH 463/569] Rise Bid Adapter: add support for mimes, api, protocols in bid object (#9253) * added support to mimes, api and protocols based on bid settings * moved protocols only if object is video * updated path for protocols property * added unit test for api, mimes and protocols properties --- modules/riseBidAdapter.js | 15 +++++++++++++++ test/spec/modules/riseBidAdapter_spec.js | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index c1360b79066..4325a699a3b 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -309,6 +309,16 @@ function generateBidParameters(bid, bidderRequest) { bidObject.placementId = placementId; } + const mimes = deepAccess(bid, `mediaTypes.${mediaType}.mimes`); + if (mimes) { + bidObject.mimes = mimes; + } + + const api = deepAccess(bid, `mediaTypes.${mediaType}.api`); + if (api) { + bidObject.api = api; + } + if (mediaType === VIDEO) { const playbackMethod = deepAccess(bid, `mediaTypes.video.playbackmethod`); let playbackMethodValue; @@ -349,6 +359,11 @@ function generateBidParameters(bid, bidderRequest) { if (linearity) { bidObject.linearity = linearity; } + + const protocols = deepAccess(bid, `mediaTypes.video.protocols`); + if (protocols) { + bidObject.protocols = protocols; + } } return bidObject; diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index 7daedb9015a..37100073407 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -106,6 +106,9 @@ describe('riseAdapter', function () { bidderCode: 'rise', } const placementId = '12345678'; + const api = [1, 2]; + const mimes = ['application/javascript', 'video/mp4', 'video/quicktime']; + const protocols = [2, 3, 5, 6]; it('sends the placementId to ENDPOINT via POST', function () { bidRequests[0].params.placementId = placementId; @@ -144,6 +147,27 @@ describe('riseAdapter', function () { expect(request.data.bids[0].bidId).to.equal('299ffc8cca0b87'); }); + it('should send the correct supported api array', function () { + bidRequests[0].mediaTypes.video.api = api; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].api).to.be.an('array'); + expect(request.data.bids[0].api).to.eql([1, 2]); + }); + + it('should send the correct mimes array', function () { + bidRequests[1].mediaTypes.banner.mimes = mimes; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[1].mimes).to.be.an('array'); + expect(request.data.bids[1].mimes).to.eql(['application/javascript', 'video/mp4', 'video/quicktime']); + }); + + it('should send the correct protocols array', function () { + bidRequests[0].mediaTypes.video.protocols = protocols; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].protocols).to.be.an('array'); + expect(request.data.bids[0].protocols).to.eql([2, 3, 5, 6]); + }); + it('should send the correct sizes array', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.bids[0].sizes).to.be.an('array'); From 5b0bb17d98137a2c38b7dceb8fc1bd291ef999e8 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 22 Nov 2022 12:23:20 -0700 Subject: [PATCH 464/569] Prebid core: fix CPM to always be a number (#9273) --- src/auction.js | 2 +- test/spec/auctionmanager_spec.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/auction.js b/src/auction.js index 09fb275afdb..6376457c1d6 100644 --- a/src/auction.js +++ b/src/auction.js @@ -672,7 +672,7 @@ function addCommonResponseProperties(bidResponse, adUnitCode, {index = auctionMa Object.assign(bidResponse, { responseTimestamp: bidResponse.responseTimestamp || timestamp(), requestTimestamp: bidResponse.requestTimestamp || start, - cpm: bidResponse.cpm || parseFloat(bidResponse.cpm) || 0, + cpm: parseFloat(bidResponse.cpm) || 0, bidder: bidResponse.bidder || bidResponse.bidderCode, adUnitCode }); diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 47bdd4a3772..443147f5d20 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -1464,6 +1464,15 @@ describe('auctionmanager.js', function () { assert.equal(doneSpy.callCount, 1); }); + it('should convert cpm to number', () => { + auction.addBidReceived = sinon.spy(); + const cbs = auctionCallbacks(doneSpy, auction); + const bid = {...bids[0], cpm: '1.23'} + bidRequests = [mockBidRequest(bid)]; + cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE, bid); + sinon.assert.calledWith(auction.addBidReceived, sinon.match({cpm: 1.23})); + }) + describe('when addBidResponse hook returns promises', () => { let resolvers, callbacks, bids; @@ -1605,6 +1614,7 @@ describe('auctionmanager.js', function () { ]; cbs = auctionCallbacks(doneSpy, auction); expectedRejection = sinon.match(Object.assign({}, bid, { + cpm: parseFloat(bid.cpm), rejectionReason: REJECTION_REASON, adUnitCode: AU_CODE })); From 737646f9f77f18952fd228e1114f0ed2b6e229c2 Mon Sep 17 00:00:00 2001 From: Gabriel Chicoye Date: Thu, 24 Nov 2022 11:49:58 +0100 Subject: [PATCH 465/569] nexx360 Bid Adapter: new functionalities and endpoint update (#9229) * bidder version v1.0 * Comments removed * Test coverage improvement above 80% --- modules/nexx360BidAdapter.js | 402 ++++++++---- test/spec/modules/nexx360BidAdapter_spec.js | 662 +++++++++++++------- 2 files changed, 705 insertions(+), 359 deletions(-) diff --git a/modules/nexx360BidAdapter.js b/modules/nexx360BidAdapter.js index ed0c0c66a7a..ff4ca77aac2 100644 --- a/modules/nexx360BidAdapter.js +++ b/modules/nexx360BidAdapter.js @@ -1,176 +1,302 @@ -import {ajax} from '../src/ajax.js'; import {config} from '../src/config.js'; -import { transformBidderParamKeywords } from '../src/utils.js'; +import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {getGlobal} from '../src/prebidGlobal.js'; +const VIDEO_TARGETING = ['startdelay', 'mimes', 'minduration', 'maxduration', 'delivery', + 'startdelay', 'skip', 'playbackmethod', 'api', 'protocol', 'boxingallowed', 'maxextended', + 'linearity', 'delivery', 'protocols', 'placement', 'minbitrate', 'maxbitrate', 'battr', 'ext']; const BIDDER_CODE = 'nexx360'; -const BIDDER_URL = 'https://fast.nexx360.io/prebid'; -const CACHE_URL = 'https://fast.nexx360.io/cache'; -const METRICS_TRACKER_URL = 'https://fast.nexx360.io/track-imp'; +const REQUEST_URL = 'https://fast.nexx360.io/booster'; + +const PAGE_VIEW_ID = utils.generateUUID(); + +const BIDDER_VERSION = '1.0'; const GVLID = 965; export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: ['revenuemaker'], // short code + aliases: [ + { code: 'revenuemaker' }, + { code: 'firstid-ssp' }, + ], supportedMediaTypes: [BANNER, VIDEO], - /** + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, + // onBidWon, +}; + +registerBidder(spec); + +/** * Determines whether or not the given bid request is valid. * * @param {BidRequest} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. */ - isBidRequestValid: function(bid) { - if (!!bid.params.bidfloorCurrency && !['EUR', 'USD'].includes(bid.params.bidfloorCurrency)) return false; - if (!!bid.params.bidfloor && typeof bid.params.bidfloor !== 'number') return false; - if (!!bid.params.keywords && typeof bid.params.keywords !== 'object') return false; - return !!(bid.params.account && bid.params.tagId); - }, - /** +function isBidRequestValid(bid) { + return !!bid.params.tagId || !!bid.params.videoTagId; +}; + +/** * Make a server request from the list of BidRequests. * * @param {validBidRequests[]} - an array of bids * @return ServerRequest Info describing the request to the server. */ - buildRequests: function(validBidRequests, bidderRequest) { - const adUnits = []; - const test = config.getConfig('debug') ? 1 : 0; - let adunitValue = null; - let userEids = null; - Object.keys(validBidRequests).forEach(key => { - adunitValue = validBidRequests[key]; - const foo = { - account: adunitValue.params.account, - tagId: adunitValue.params.tagId, - videoExt: adunitValue.params.videoExt, - label: adunitValue.adUnitCode, - bidId: adunitValue.bidId, - auctionId: adunitValue.auctionId, - transactionId: adunitValue.transactionId, - mediatypes: adunitValue.mediaTypes, - bidfloor: adunitValue.params.bidfloor || 0, - bidfloorCurrency: adunitValue.params.bidfloorCurrency || 'USD', - keywords: adunitValue.params.keywords ? transformBidderParamKeywords(adunitValue.params.keywords) : [], + +function buildRequests(bids, bidderRequest) { + const data = getBaseRequest(bids[0], bidderRequest); + bids.forEach((bid) => { + const impObject = createImpObject(bid); + if (isBannerBid(bid)) impObject.banner = getBannerObject(bid); + if (isVideoBid(bid)) impObject.video = getVideoObject(bid); + data.imp.push(impObject); + }); + return { + method: 'POST', + url: REQUEST_URL, + data, + } +} + +function createImpObject(bid) { + const floor = getFloor(bid, BANNER); + const imp = { + id: bid.bidId, + tagid: bid.adUnitCode, + ext: { + divId: bid.adUnitCode, + nexx360: { + videoTagId: bid.params.videoTagId, + tagId: bid.params.tagId, + allBids: bid.params.allBids === true, } - adUnits.push(foo); - if (adunitValue.userIdAsEids) userEids = adunitValue.userIdAsEids; + } + }; + if (bid.params.customParams) { + utils.deepSetValue(imp, 'ext.customParams', bid.params.customParams); + } + enrichImp(imp, bid, floor); + return imp; +} + +function getBannerObject(bid) { + return { + format: toFormat(bid.mediaTypes.banner.sizes), + topframe: utils.inIframe() ? 0 : 1 + }; +} + +function getVideoObject(bid) { + let width, height; + const videoParams = utils.deepAccess(bid, `mediaTypes.video`); + const playerSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize'); + const context = utils.deepAccess(bid, 'mediaTypes.video.context'); + // normalize config for video size + if (utils.isArray(bid.sizes) && bid.sizes.length === 2 && !utils.isArray(bid.sizes[0])) { + width = parseInt(bid.sizes[0], 10); + height = parseInt(bid.sizes[1], 10); + } else if (utils.isArray(bid.sizes) && utils.isArray(bid.sizes[0]) && bid.sizes[0].length === 2) { + width = parseInt(bid.sizes[0][0], 10); + height = parseInt(bid.sizes[0][1], 10); + } else if (utils.isArray(playerSize) && playerSize.length === 2) { + width = parseInt(playerSize[0], 10); + height = parseInt(playerSize[1], 10); + } + const video = { + playerSize: [height, width], + context, + }; + + Object.keys(videoParams) + .filter(param => VIDEO_TARGETING.includes(param)) + .forEach(param => video[param] = videoParams[param]); + return video; +} + +function isVideoBid(bid) { + return utils.deepAccess(bid, 'mediaTypes.video'); +} + +function isBannerBid(bid) { + return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid); +} + +function toFormat(sizes) { + return sizes.map((s) => { + return { w: s[0], h: s[1] }; + }); +} + +function getFloor(bid, mediaType) { + let floor = 0; + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: mediaType, + size: '*' }); - const payload = { - adUnits, - // TODO: does the fallback make sense here? - href: encodeURIComponent(bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation) - }; - if (bidderRequest) { // modules informations (gdpr, ccpa, schain, userId) - if (bidderRequest.gdprConsent) { - payload.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - payload.gdprConsent = bidderRequest.gdprConsent.consentString; - } else { - payload.gdpr = 0; - payload.gdprConsent = ''; - } - if (bidderRequest.uspConsent) { payload.uspConsent = bidderRequest.uspConsent; } - if (bidderRequest.schain) { payload.schain = bidderRequest.schain; } - if (userEids !== null) payload.userEids = userEids; - }; - if (test) payload.test = 1; - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: BIDDER_URL, - data: payloadString, - }; - }, - /** + + if (typeof floorInfo === 'object' && + floorInfo.currency === 'USD' && + !isNaN(parseFloat(floorInfo.floor))) { + floor = Math.max(floor, parseFloat(floorInfo.floor)); + } + } + + return floor; +} + +function enrichImp(imp, bid, floor) { + if (bid.params.customParams) { + utils.deepSetValue(imp, 'ext.customParams', bid.params.customParams); + } + if (floor > 0) { + imp.bidfloor = floor; + imp.bidfloorcur = 'USD'; + } else if (bid.params.customFloor) { + imp.bidfloor = bid.params.customFloor; + } + if (bid.ortb2Imp && bid.ortb2Imp.ext && bid.ortb2Imp.ext.data) { + imp.ext.data = bid.ortb2Imp.ext.data; + } +} + +function getBaseRequest(bid, bidderRequest) { + let req = { + id: bidderRequest.auctionId, + imp: [], + cur: [config.getConfig('currency.adServerCurrency') || 'USD'], + at: 1, + tmax: config.getConfig('bidderTimeout'), + site: { + page: bidderRequest.refererInfo.topmostLocation || bidderRequest.refererInfo.page, + domain: bidderRequest.refererInfo.domain, + }, + regs: { + coppa: (config.getConfig('coppa') === true || bid.params.coppa) ? 1 : 0, + }, + device: { + dnt: (utils.getDNT() || bid.params.doNotTrack) ? 1 : 0, + h: screen.height, + w: screen.width, + ua: window.navigator.userAgent, + language: window.navigator.language.split('-').shift() + }, + user: {}, + ext: { + source: 'prebid.js', + version: '$prebid.version$', + pageViewId: PAGE_VIEW_ID, + bidderVersion: BIDDER_VERSION, + } + }; + + if (bid.params.platform) { + utils.deepSetValue(req, 'ext.platform', bid.params.platform); + } + if (bid.params.response_template_name) { + utils.deepSetValue(req, 'ext.response_template_name', bid.params.response_template_name); + } + req.test = config.getConfig('debug') ? 1 : 0; + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.gdprApplies !== undefined) { + utils.deepSetValue(req, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies === true ? 1 : 0); + } + if (bidderRequest.gdprConsent.consentString !== undefined) { + utils.deepSetValue(req, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + } + if (bidderRequest.gdprConsent.addtlConsent !== undefined) { + utils.deepSetValue(req, 'user.ext.ConsentedProvidersSettings.consented_providers', bidderRequest.gdprConsent.addtlConsent); + } + } + if (bidderRequest.uspConsent) { + utils.deepSetValue(req, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + if (bid.schain) { + utils.deepSetValue(req, 'source.ext.schain', bid.schain); + } + if (bid.userIdAsEids) { + utils.deepSetValue(req, 'user.ext.eids', bid.userIdAsEids); + } + const commonFpd = bidderRequest.ortb2 || {}; + if (commonFpd.site) { + utils.mergeDeep(req, {site: commonFpd.site}); + } + if (commonFpd.user) { + utils.mergeDeep(req, {user: commonFpd.user}); + } + return req; +} + +/** * Unpack the response from the server into a list of bids. * * @param {ServerResponse} serverResponse A successful response from the server. * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse, bidRequest) { - const serverBody = serverResponse.body; - const bidResponses = []; - let bidResponse = null; - let value = null; - if (serverBody.hasOwnProperty('responses')) { - Object.keys(serverBody['responses']).forEach(key => { - value = serverBody['responses'][key]; - const url = `${CACHE_URL}?uuid=${value['uuid']}`; - bidResponse = { - requestId: value['bidId'], - cpm: value['cpm'], - currency: value['currency'], - width: value['width'], - height: value['height'], - ttl: value['ttl'], - creativeId: value['creativeId'], - netRevenue: true, - nexx360: { - 'ssp': value['bidder'], - 'consent': value['consent'], - 'tagId': value['tagId'] - }, - /* - meta: { - 'advertiserDomains': value['adomain'] - } - */ - }; - if (value.type === 'banner') bidResponse.adUrl = url; - if (value.type === 'video') { - const params = { - type: 'prebid', - mediatype: 'video', - ssp: value.bidder, - tag_id: value.tagId, - consent: value.consent, - price: value.cpm, - }; - bidResponse.cpm = value.cpm; - bidResponse.mediaType = 'video'; - bidResponse.vastUrl = url; - bidResponse.vastImpUrl = `${METRICS_TRACKER_URL}?${new URLSearchParams(params).toString()}`; - } - bidResponses.push(bidResponse); - }); - } - return bidResponses; - }, +function interpretResponse(response, req) { + const { bidderSettings } = getGlobal(); + const allowAlternateBidderCodes = bidderSettings && bidderSettings.standard ? bidderSettings.standard.allowAlternateBidderCodes : false; + const respBody = response.body; + if (!respBody || !Array.isArray(respBody.seatbid)) { + return []; + } + + let bids = []; + respBody.seatbid.forEach(seatbid => { + bids = [...bids, ...seatbid.bid.map(bid => { + const response = { + requestId: bid.impid, + cpm: bid.price, + width: bid.w, + height: bid.h, + creativeId: bid.crid, + dealId: bid.dealid, + currency: respBody.cur || 'USD', + netRevenue: true, + ttl: 120, + bidderCode: allowAlternateBidderCodes ? `n360-${bid.ssp}` : 'nexx360', + mediaType: bid.type === 'banner' ? 'banner' : 'video', + meta: { advertiserDomains: bid.adomain }, + }; + // if (bid.dealid) response.dealid = bid.dealid; - /** + if (response.mediaType === 'banner') { + response.adUrl = bid.adUrl; + } + + if (['instream', 'outstream'].includes(bid.type)) response.vastXml = bid.vastXml; + + if (bid.ext) { + response.meta.networkId = bid.ext.dsp_id; + response.meta.advertiserId = bid.ext.buyer_id; + response.meta.brandId = bid.ext.brand_id; + } + return response; + })]; + }); + return bids; +} + +/** * Register the user sync pixels which should be dropped after the auction. * * @param {SyncOptions} syncOptions Which user syncs are allowed? * @param {ServerResponse[]} serverResponses List of server's responses. * @return {UserSync[]} The user syncs which should be dropped. */ - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { - if (typeof serverResponses === 'object' && serverResponses != null && serverResponses.length > 0 && serverResponses[0].hasOwnProperty('body') && +function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { + if (typeof serverResponses === 'object' && serverResponses != null && serverResponses.length > 0 && serverResponses[0].hasOwnProperty('body') && serverResponses[0].body.hasOwnProperty('cookies') && typeof serverResponses[0].body.cookies === 'object') { - return serverResponses[0].body.cookies.slice(0, 5); - } else { - return []; - } - }, - - /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction - * @param {Bid} The bid that won the auction - */ - onBidWon: function(bid) { - // fires a pixel to confirm a winning bid - const params = { type: 'prebid', mediatype: 'banner' }; - if (bid.hasOwnProperty('nexx360')) { - if (bid.nexx360.hasOwnProperty('ssp')) params.ssp = bid.nexx360.ssp; - if (bid.nexx360.hasOwnProperty('tagId')) params.tag_id = bid.nexx360.tagId; - if (bid.nexx360.hasOwnProperty('consent')) params.consent = bid.nexx360.consent; - }; - params.price = bid.cpm; - const url = `${METRICS_TRACKER_URL}?${new URLSearchParams(params).toString()}`; - ajax(url, null, undefined, {method: 'GET', withCredentials: true}); - return true; + return serverResponses[0].body.cookies.slice(0, 5); + } else { + return []; } - -} -registerBidder(spec); +}; diff --git a/test/spec/modules/nexx360BidAdapter_spec.js b/test/spec/modules/nexx360BidAdapter_spec.js index 1a695e5f8b3..a5da8b29fbc 100644 --- a/test/spec/modules/nexx360BidAdapter_spec.js +++ b/test/spec/modules/nexx360BidAdapter_spec.js @@ -6,70 +6,39 @@ import * as utils from 'src/utils.js'; import { requestBidsHook } from 'modules/consentManagement.js'; describe('Nexx360 bid adapter tests', function () { - const DISPLAY_BID_REQUEST = [{ - 'bidder': 'nexx360', - 'params': { - 'account': '1067', - 'tagId': 'luvxjvgn' + const DISPLAY_BID_REQUEST = { + 'id': '77b3f21a-e0df-4495-8bce-4e8a1d2309c1', + 'imp': [ + {'id': '2b4d8fc1c1c7ea', + 'tagid': 'div-1', + 'ext': {'divId': 'div-1', 'nexx360': {'account': '1067', 'tag_id': 'luvxjvgn'}}, + 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}], 'topframe': 1}}, {'id': '38fc428ab96638', 'tagid': 'div-2', 'ext': {'divId': 'div-2', 'nexx360': {'account': '1067', 'tag_id': 'luvxjvgn'}}, 'banner': {'format': [{'w': 728, 'h': 90}, {'w': 970, 'h': 250}], 'topframe': 1}}], + 'cur': ['USD'], + 'at': 1, + 'tmax': 3000, + 'site': {'page': 'https://test.nexx360.io/adapter/index.html?nexx360_test=1', 'domain': 'test.nexx360.io'}, + 'regs': {'coppa': 0, 'ext': {'gdpr': 1}}, + 'device': { + 'dnt': 0, + 'h': 844, + 'w': 390, + 'ua': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', + 'language': 'fr' }, - 'userId': { - 'id5id': { - 'uid': 'ID5*hQ5WobYI9Od4u52qpaXVKHhxUa4DsOWRAlvaFajm8gINfI1oVAe3UK59416dT4TqDX1pj4MBJ5TYwir6x3JgBw1-avYHSnmvQDdRMbxmC2sNf3ggIRTbyQBdI1RjvHyeDYCsistnTXF_iKF1nutYeQ2BZ4P5d5muZTG7C2PXVFgNg-18io9dCiSjzJXx93KPDYRiuIwtsGGsp51rojlpFw2Fp_dUkjXl4CAblk58DvwNhobwQ27bnBP8F2-Pcs88DYcvKn4r6dm3Vi7ILttxDQ2IgZ2X44ClgjoWh-vRf6ANis8Z7uL16vO8q0P5C21eDYuc4v_KaZqN-p9YWEeEZQ2OpkbRL7n5NieVJExHM6ANkAlLZhVf2T-1906TAIHKDZFm_xMCa1jJfpBqZB2agw2TjfbK6wMtJeHiZaipSuUNlM_CSH0HVXtfMj9yfzjzDZZnltZQ9lvc4JhXye5AwA2X1f9Dhk8VURTvVdfEUlU', - 'ext': { - 'linkType': 2 - } - } - }, - 'userIdAsEids': [ - { - 'source': 'id5-sync.com', - 'uids': [ - { - 'id': 'ID5*hQ5WobYI9Od4u52qpaXVKHhxUa4DsOWRAlvaFajm8gINfI1oVAe3UK59416dT4TqDX1pj4MBJ5TYwir6x3JgBw1-avYHSnmvQDdRMbxmC2sNf3ggIRTbyQBdI1RjvHyeDYCsistnTXF_iKF1nutYeQ2BZ4P5d5muZTG7C2PXVFgNg-18io9dCiSjzJXx93KPDYRiuIwtsGGsp51rojlpFw2Fp_dUkjXl4CAblk58DvwNhobwQ27bnBP8F2-Pcs88DYcvKn4r6dm3Vi7ILttxDQ2IgZ2X44ClgjoWh-vRf6ANis8Z7uL16vO8q0P5C21eDYuc4v_KaZqN-p9YWEeEZQ2OpkbRL7n5NieVJExHM6ANkAlLZhVf2T-1906TAIHKDZFm_xMCa1jJfpBqZB2agw2TjfbK6wMtJeHiZaipSuUNlM_CSH0HVXtfMj9yfzjzDZZnltZQ9lvc4JhXye5AwA2X1f9Dhk8VURTvVdfEUlU', + 'user': { + 'ext': { + 'consent': 'CPgocUAPgocUAAKAsAENCkCsAP_AAH_AAAqIJDtd_H__bW9r-f5_aft0eY1P9_r37uQzDhfNk-8F3L_W_LwX52E7NF36tq4KmR4ku1LBIUNlHMHUDUmwaokVryHsak2cpzNKJ7BEknMZOydYGF9vmxtj-QKY7_5_d3bx2D-t_9v239z3z81Xn3d53-_03LCdV5_9Dfn9fR_bc9KPt_58v8v8_____3_e__3_7994JEAEmGrcQBdmWODNoGEUCIEYVhIVQKACCgGFogMAHBwU7KwCfWECABAKAIwIgQ4AowIBAAAJAEhEAEgRYIAAARAIAAQAIhEIAGBgEFgBYGAQAAgGgYohQACBIQZEBEUpgQFQJBAa2VCCUF0hphAHWWAFBIjYqABEEgIrAAEBYOAYIkBKxYIEmKN8gBGCFAKJUK1EAAAA.YAAAAAAAAAAA', + 'ConsentedProvidersSettings': {'consented_providers': '1~39.43.46.55.61.70.83.89.93.108.117.122.124.131.135.136.143.144.147.149.159.162.167.171.192.196.202.211.218.228.230.239.241.259.266.272.286.291.311.317.322.323.326.327.338.367.371.385.389.394.397.407.413.415.424.430.436.445.449.453.482.486.491.494.495.501.503.505.522.523.540.550.559.560.568.574.576.584.587.591.733.737.745.787.802.803.817.820.821.829.839.864.867.874.899.904.922.931.938.979.981.985.1003.1024.1027.1031.1033.1040.1046.1051.1053.1067.1085.1092.1095.1097.1099.1107.1127.1135.1143.1149.1152.1162.1166.1186.1188.1201.1205.1211.1215.1226.1227.1230.1252.1268.1270.1276.1284.1286.1290.1301.1307.1312.1345.1356.1364.1365.1375.1403.1415.1416.1419.1440.1442.1449.1455.1456.1465.1495.1512.1516.1525.1540.1548.1555.1558.1564.1570.1577.1579.1583.1584.1591.1603.1616.1638.1651.1653.1665.1667.1677.1678.1682.1697.1699.1703.1712.1716.1721.1725.1732.1745.1750.1765.1769.1782.1786.1800.1808.1810.1825.1827.1832.1838.1840.1842.1843.1845.1859.1866.1870.1878.1880.1889.1899.1917.1929.1942.1944.1962.1963.1964.1967.1968.1969.1978.2003.2007.2008.2027.2035.2039.2044.2047.2052.2056.2064.2068.2070.2072.2074.2088.2090.2103.2107.2109.2115.2124.2130.2133.2137.2140.2145.2147.2150.2156.2166.2177.2183.2186.2202.2205.2216.2219.2220.2222.2225.2234.2253.2264.2279.2282.2292.2299.2305.2309.2312.2316.2322.2325.2328.2331.2334.2335.2336.2337.2343.2354.2357.2358.2359.2370.2376.2377.2387.2392.2394.2400.2403.2405.2407.2411.2414.2416.2418.2425.2440.2447.2459.2461.2462.2465.2468.2472.2477.2481.2484.2486.2488.2493.2496.2497.2498.2499.2501.2510.2511.2517.2526.2527.2532.2534.2535.2542.2552.2563.2564.2567.2568.2569.2571.2572.2575.2577.2583.2584.2596.2601.2604.2605.2608.2609.2610.2612.2614.2621.2628.2629.2633.2634.2636.2642.2643.2645.2646.2647.2650.2651.2652.2656.2657.2658.2660.2661.2669.2670.2677.2681.2684.2686.2687.2690.2695.2698.2707.2713.2714.2729.2739.2767.2768.2770.2772.2784.2787.2791.2792.2798.2801.2805.2812.2813.2816.2817.2818.2821.2822.2827.2830.2831.2834.2838.2839.2840.2844.2846.2847.2849.2850.2852.2854.2856.2860.2862.2863.2865.2867.2869.2873.2874.2875.2876.2878.2880.2881.2882.2883.2884.2886.2887.2888.2889.2891.2893.2894.2895.2897.2898.2900.2901.2908.2909.2911.2912.2913.2914.2916.2917.2918.2919.2920.2922.2923.2924.2927.2929.2930.2931.2939.2940.2941.2947.2949.2950.2956.2961.2962.2963.2964.2965.2966.2968.2970.2973.2974.2975.2979.2980.2981.2983.2985.2986.2987.2991.2994.2995.2997.2999.3000.3002.3003.3005.3008.3009.3010.3012.3016.3017.3018.3019.3024.3025.3028.3034.3037.3038.3043.3045.3048.3052.3053.3055.3058.3059.3063.3065.3066.3068.3070.3072.3073.3074.3075.3076.3077.3078.3089.3090.3093.3094.3095.3097.3099.3104.3106.3109.3112.3117.3118.3119.3120.3124.3126.3127.3128.3130.3135.3136.3145.3149.3150.3151.3154.3155.3162.3163.3167.3172.3173.3180.3182.3183.3184.3185.3187.3188.3189.3190.3194.3196.3197.3209.3210.3211.3214.3215.3217.3219.3222.3223.3225.3226.3227.3228.3230.3231.3232.3234.3235.3236.3237.3238.3240.3241.3244.3245.3250.3251.3253.3257.3260.3268.3270.3272.3281.3288.3290.3292.3293.3295.3296.3300.3306.3307.3308.3314.3315.3316.3318.3324.3327.3328.3330'}, + 'eids': [{'source': 'id5-sync.com', + 'uids': [{'id': 'ID5*tdrSpYbccONIbxmulXFRLEil1aozZGGVMo9eEZgydgYoYFZQRYoae3wJyY0YtmXGKGJ7uXIQByQ6f7uzcpy9Oyhj1jGRzCf0BCoI4VkkKZIoZBubolUKUXXxOIdQOz7ZKGV0E3sqi9Zut0BbOuoJAihpLbgfNgDJ0xRmQw04rDooaxn7_TIPzEX5_L5ohNkUKG01Gnh2djvcrcPigKlk7ChwnauCwHIetHYI32yYAnAocYyqoM9XkoVOHtyOTC_UKHIR0qVBVIzJ1Nn_g7kLqyhzfosadKVvf7RQCsE6QrYodtpOJKg7i72-tnMXkzgmKHjh98aEDfTQrZOkKebmAyh6GlOHtYn_sZBFjJwtWp4oe9j2QTNbzK3G0jp1PlJqKHxiu4LawFEKJ3yi5-NFUyh-YkEalJUWyl1cDlWo5NQogAy2HM8N_w0qrVQgNbrTKIHK3KzTXztH7WzBgYrk8g', 'atype': 1, - 'ext': { - 'linkType': 2 - } - } - ] - } - ], - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [300, 600]] - } - }, - 'adUnitCode': 'banner-div', - 'transactionId': '9ad89d90-eb73-41b9-bf5f-7a8e2eecff27', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '4d9e29504f8af6', - 'bidderRequestId': '3423b6bd1a922c', - 'auctionId': '05e0a3a1-9f57-41f6-bbcb-2ba9c9e3d2d5', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - }]; - - const DISPLAY_BID_RESPONSE = {'body': { - 'responses': [ - { - 'bidId': '4d9e29504f8af6', - 'cpm': 0.437245, - 'width': 300, - 'height': 250, - 'creativeId': '98493581', - 'currency': 'EUR', - 'netRevenue': true, - 'type': 'banner', - 'ttl': 360, - 'uuid': 'ce6d1ee3-2a05-4d7c-b97a-9e62097798ec', - 'bidder': 'appnexus', - 'consent': 1, - 'tagId': 'luvxjvgn' - } - ], - }}; + 'ext': {'linkType': 2}}]}, + {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}]}}, + 'ext': { + 'source': 'prebid.js', + 'version': '7.20.0-pre', + 'pageViewId': '5b970aba-51e9-4e0a-8299-f3f5618c695e' + }} const VIDEO_BID_REQUEST = [ { @@ -101,26 +70,6 @@ describe('Nexx360 bid adapter tests', function () { } ] - const VIDEO_BID_RESPONSE = {'body': { - 'responses': [ - { - 'bidId': '2c129e8e01859a', - 'type': 'video', - 'uuid': 'b8e7b2f0-c378-479f-aa4f-4f55d5d7d1d5', - 'cpm': 4.5421, - 'width': 1, - 'height': 1, - 'creativeId': '97517771', - 'currency': 'EUR', - 'netRevenue': true, - 'ttl': 360, - 'bidder': 'appnexus', - 'consent': 1, - 'tagId': 'yqsc1tfj' - } - ] - }}; - const DEFAULT_OPTIONS = { gdprConsent: { gdprApplies: true, @@ -147,159 +96,430 @@ describe('Nexx360 bid adapter tests', function () { }, }; - it('We verify isBidRequestValid with uncorrect bidfloorCurrency', function() { - const bid = { params: { - 'account': '1067', - 'tagId': 'luvxjvgn', - 'bidfloorCurrency': 'AAA', - }}; - expect(spec.isBidRequestValid(bid)).to.be.equal(false); - }); + describe('isBidRequestValid()', function() { + let bannerBid; + beforeEach(function () { + bannerBid = { + 'bidder': 'nexx360', + 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, + 'adUnitCode': 'div-1', + 'transactionId': '70bdc37e-9475-4b27-8c74-4634bdc2ee66', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4906582fc87d0c', + 'bidderRequestId': '332fda16002dbe', + 'auctionId': '98932591-c822-42e3-850e-4b3cf748d063', + } + }); + it('We verify isBidRequestValid with uncorrect tagid', function() { + bannerBid.params = { 'tagid': 'luvxjvgn' }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); - it('We verify isBidRequestValid with uncorrect bidfloor', function() { - const bid = { params: { - 'account': '1067', - 'tagId': 'luvxjvgn', - 'bidfloorCurrency': 'EUR', - 'bidfloor': 'EUR', - }}; - expect(spec.isBidRequestValid(bid)).to.be.equal(false); + it('We verify isBidRequestValid with correct tagId', function() { + bannerBid.params = { 'tagId': 'luvxjvgn' }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(true); + }); }); - it('We verify isBidRequestValid with uncorrect keywords', function() { - const bid = { params: { - 'account': '1067', - 'tagId': 'luvxjvgn', - 'bidfloorCurrency': 'EUR', - 'bidfloor': 0.8, - 'keywords': 'test', - }}; - expect(spec.isBidRequestValid(bid)).to.be.equal(false); + describe('when request is for a multiformat ad', function () { + describe('and request config uses mediaTypes video and banner', () => { + const multiformatBid = { + 'bidder': 'nexx360', + 'params': {'tagId': 'luvxjvgn'}, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + }, + 'video': { + 'playerSize': [300, 250] + } + }, + 'adUnitCode': 'div-1', + 'transactionId': '70bdc37e-9475-4b27-8c74-4634bdc2ee66', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4906582fc87d0c', + 'bidderRequestId': '332fda16002dbe', + 'auctionId': '98932591-c822-42e3-850e-4b3cf748d063', + } + it('should return true multisize when required params found', function () { + expect(spec.isBidRequestValid(multiformatBid)).to.equal(true); + }); + }); }); - it('Verify banner build request', function () { - const request = spec.buildRequests(DISPLAY_BID_REQUEST, DEFAULT_OPTIONS); - expect(request).to.have.property('url').and.to.equal('https://fast.nexx360.io/prebid'); - expect(request).to.have.property('method').and.to.equal('POST'); - const requestContent = JSON.parse(request.data); - expect(requestContent.userEids.length).to.be.eql(1); - expect(requestContent.userEids[0]).to.have.property('source').and.to.equal('id5-sync.com'); - expect(requestContent.userEids[0]).to.have.property('uids'); - expect(requestContent.userEids[0].uids[0]).to.have.property('id').and.to.equal('ID5*hQ5WobYI9Od4u52qpaXVKHhxUa4DsOWRAlvaFajm8gINfI1oVAe3UK59416dT4TqDX1pj4MBJ5TYwir6x3JgBw1-avYHSnmvQDdRMbxmC2sNf3ggIRTbyQBdI1RjvHyeDYCsistnTXF_iKF1nutYeQ2BZ4P5d5muZTG7C2PXVFgNg-18io9dCiSjzJXx93KPDYRiuIwtsGGsp51rojlpFw2Fp_dUkjXl4CAblk58DvwNhobwQ27bnBP8F2-Pcs88DYcvKn4r6dm3Vi7ILttxDQ2IgZ2X44ClgjoWh-vRf6ANis8Z7uL16vO8q0P5C21eDYuc4v_KaZqN-p9YWEeEZQ2OpkbRL7n5NieVJExHM6ANkAlLZhVf2T-1906TAIHKDZFm_xMCa1jJfpBqZB2agw2TjfbK6wMtJeHiZaipSuUNlM_CSH0HVXtfMj9yfzjzDZZnltZQ9lvc4JhXye5AwA2X1f9Dhk8VURTvVdfEUlU'); - expect(requestContent.adUnits[0]).to.have.property('account').and.to.equal('1067'); - expect(requestContent.adUnits[0]).to.have.property('tagId').and.to.equal('luvxjvgn'); - expect(requestContent.adUnits[0]).to.have.property('label').and.to.equal('banner-div'); - expect(requestContent.adUnits[0]).to.have.property('bidId').and.to.equal('4d9e29504f8af6'); - expect(requestContent.adUnits[0]).to.have.property('auctionId').and.to.equal('05e0a3a1-9f57-41f6-bbcb-2ba9c9e3d2d5'); - expect(requestContent.adUnits[0]).to.have.property('mediatypes').exist; - expect(requestContent.adUnits[0].mediatypes).to.have.property('banner').exist; - expect(requestContent.adUnits[0]).to.have.property('bidfloor').and.to.equal(0); - expect(requestContent.adUnits[0]).to.have.property('bidfloorCurrency').and.to.equal('USD'); - expect(requestContent.adUnits[0]).to.have.property('keywords'); - expect(requestContent.adUnits[0].keywords.length).to.be.eql(0); + describe('when request is for a video ad', function () { + describe('and request config uses mediaTypes video and banner', () => { + const videoBid = { + 'bidder': 'nexx360', + 'params': {'tagId': 'luvxjvgn'}, + 'mediaTypes': { + 'video': { + 'playerSize': [300, 250] + } + }, + 'adUnitCode': 'div-1', + 'transactionId': '70bdc37e-9475-4b27-8c74-4634bdc2ee66', + 'bidId': '4906582fc87d0c', + 'bidderRequestId': '332fda16002dbe', + 'auctionId': '98932591-c822-42e3-850e-4b3cf748d063', + } + it('should return true multisize when required params found', function () { + expect(spec.isBidRequestValid(videoBid)).to.equal(true); + }); + }); }); - it('We add bidfloor and keywords', function() { - const DISPLAY_BID_REQUEST_2 = [ ...DISPLAY_BID_REQUEST ]; - DISPLAY_BID_REQUEST_2[0].params.keywords = { interest: 'cars' }; - DISPLAY_BID_REQUEST_2[0].params.bidfloor = 2.1; - const request = spec.buildRequests(DISPLAY_BID_REQUEST, DEFAULT_OPTIONS); - const requestContent = JSON.parse(request.data); - expect(requestContent.adUnits[0]).to.have.property('bidfloor').and.to.equal(2.1); - expect(requestContent.adUnits[0]).to.have.property('bidfloorCurrency').and.to.equal('USD'); - expect(requestContent.adUnits[0]).to.have.property('keywords'); - expect(requestContent.adUnits[0].keywords.length).to.be.eql(1); - expect(requestContent.adUnits[0].keywords[0].key).to.be.eql('interest'); - expect(requestContent.adUnits[0].keywords[0].value[0]).to.be.eql('cars'); - }); + describe('buildRequests()', function() { + describe('We test with a multiple display bids', function() { + const sampleBids = [ + { + bidder: 'nexx360', + params: { + tagId: 'luvxjvgn' + }, + adUnitCode: 'div-1', + transactionId: '469a570d-f187-488d-b1cb-48c1a2009be9', + sizes: [[300, 250], [300, 600]], + bidId: '44a2706ac3574', + bidderRequestId: '359bf8a3c06b2e', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + userIdAsEids: [ + { + source: 'id5-sync.com', + uids: [ + { + id: 'ID5*xe3R0Pbrc5Y4WBrb5UZSWTiS1t9DU2LgQrhdZOgFdXMoglhqmjs_SfBbyHfSYGZKKIT4Gf-XOQ_anA3iqi0hJSiFyD3aICGHDJFxNS8LO84ohwTQ0EiwOexZAbBlH0chKIhbvdGBfuouNuVF_YHCoyiLQJDp3WQiH96lE9MH2T0ojRqoyR623gxAWlBCBPh7KI4bYtZlet3Vtr-gH5_xqCiSEd7aYV37wHxUTSN38Isok_0qDCHg4pKXCcVM2h6FKJSGmvw-xPm9HkfkIcbh1CiVVG4nREP142XrBecdzhQomNlcalmwdzGHsuHPjTP-KJraa15yvvZDceq-f_YfECicDllYBLEsg24oPRM-ibMonWtT9qOm5dSfWS5G_r09KJ4HMB6REICq1wleDD1mwSigXkM_nxIKa4TxRaRqEekoooWRwuKA5-euHN3xxNfIKKP19EtGhuNTs0YdCSe8_w', + atype: 1, + ext: { + linkType: 2 + } + } + ] + }, + { + source: 'domain.com', + uids: [ + { + id: 'value read from cookie or local storage', + atype: 1, + ext: { + stype: 'ppuid' + } + } + ] + } + ], + }, + { + bidder: 'nexx360', + params: { + tagId: 'luvxjvgn', + allBids: true, + }, + mediaTypes: { + banner: { + sizes: [[728, 90], [970, 250]] + } + }, + adUnitCode: 'div-2', + transactionId: '6196885d-4e76-40dc-a09c-906ed232626b', + sizes: [[728, 90], [970, 250]], + bidId: '5ba94555219a03', + bidderRequestId: '359bf8a3c06b2e', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + } + ] + const bidderRequest = { + bidderCode: 'nexx360', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + bidderRequestId: '359bf8a3c06b2e', + refererInfo: { + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: [ + 'https://test.nexx360.io/adapter/index.html' + ], + topmostLocation: 'https://test.nexx360.io/adapter/index.html', + location: 'https://test.nexx360.io/adapter/index.html', + canonicalUrl: null, + page: 'https://test.nexx360.io/adapter/index.html', + domain: 'test.nexx360.io', + ref: null, + legacy: { + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: [ + 'https://test.nexx360.io/adapter/index.html' + ], + referer: 'https://test.nexx360.io/adapter/index.html', + canonicalUrl: null + }, + }, + gdprConsent: { + gdprApplies: true, + consentString: 'CPhdLUAPhdLUAAKAsAENCmCsAP_AAE7AAAqIJFNd_H__bW9r-f5_aft0eY1P9_r37uQzDhfNk-8F3L_W_LwX52E7NF36tq4KmR4ku1LBIUNlHMHUDUmwaokVryHsak2cpzNKJ7BEknMZOydYGF9vmxtj-QKY7_5_d3bx2D-t_9v239z3z81Xn3d53-_03LCdV5_9Dfn9fR_bc9KPt_58v8v8_____3_e__3_7997BIiAaADgAJYBnwEeAJXAXmAwQBj4DtgHcgPBAeKBIgAA.YAAAAAAAAAAA', + } + }; + it('We perform a test with 2 display adunits', function() { + const displayBids = [...sampleBids]; + displayBids[0].mediaTypes = { + banner: { + sizes: [[300, 250], [300, 600]] + } + }; + const request = spec.buildRequests(displayBids, bidderRequest); + const requestContent = request.data; + expect(request).to.have.property('method').and.to.equal('POST'); + expect(requestContent.id).to.be.eql('2e684815-b44e-4e04-b812-56da54adbe74'); + expect(requestContent.cur[0]).to.be.eql('USD'); + expect(requestContent.imp.length).to.be.eql(2); + expect(requestContent.imp[0].id).to.be.eql('44a2706ac3574'); + expect(requestContent.imp[0].tagid).to.be.eql('div-1'); + expect(requestContent.imp[0].ext.divId).to.be.eql('div-1'); + expect(requestContent.imp[0].ext.nexx360.tagId).to.be.eql('luvxjvgn'); + expect(requestContent.imp[0].ext.nexx360.allBids).to.be.eql(false); + expect(requestContent.imp[0].banner.format.length).to.be.eql(2); + expect(requestContent.imp[0].banner.format[0].w).to.be.eql(300); + expect(requestContent.imp[0].banner.format[0].h).to.be.eql(250); + expect(requestContent.imp[1].ext.nexx360.allBids).to.be.eql(true); + expect(requestContent.regs.ext.gdpr).to.be.eql(1); + expect(requestContent.user.ext.consent).to.be.eql(bidderRequest.gdprConsent.consentString); + expect(requestContent.user.ext.eids.length).to.be.eql(2); + }); - it('Verify banner parse response', function () { - const request = spec.buildRequests(DISPLAY_BID_REQUEST, DEFAULT_OPTIONS); - const response = spec.interpretResponse(DISPLAY_BID_RESPONSE, request); - expect(response).to.have.lengthOf(1); - const bid = response[0]; - expect(bid.cpm).to.equal(0.437245); - expect(bid.adUrl).to.equal('https://fast.nexx360.io/cache?uuid=ce6d1ee3-2a05-4d7c-b97a-9e62097798ec'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.creativeId).to.equal('98493581'); - expect(bid.currency).to.equal('EUR'); - expect(bid.netRevenue).to.equal(true); - expect(bid.ttl).to.equal(360); - expect(bid.requestId).to.equal('4d9e29504f8af6'); - expect(bid.nexx360).to.exist; - expect(bid.nexx360.ssp).to.equal('appnexus'); - }); + it('We perform a test with a multiformat adunit', function() { + const multiformatBids = [...sampleBids]; + multiformatBids[0].mediaTypes = { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'outstream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1, + playback_method: ['auto_play_sound_off'] + } + }; + const request = spec.buildRequests(multiformatBids, bidderRequest); + const requestContent = request.data; + expect(requestContent.imp[0].video.context).to.be.eql('outstream'); + expect(requestContent.imp[0].video.playbackmethod[0]).to.be.eql(2); + }); - it('Verify video build request', function () { - const request = spec.buildRequests(VIDEO_BID_REQUEST, DEFAULT_OPTIONS); - expect(request).to.have.property('url').and.to.equal('https://fast.nexx360.io/prebid'); - expect(request).to.have.property('method').and.to.equal('POST'); - const requestContent = JSON.parse(request.data); - expect(requestContent.adUnits[0]).to.have.property('account').and.to.equal('1067'); - expect(requestContent.adUnits[0]).to.have.property('tagId').and.to.equal('yqsc1tfj'); - expect(requestContent.adUnits[0]).to.have.property('label').and.to.equal('video1'); - expect(requestContent.adUnits[0]).to.have.property('bidId').and.to.equal('22f90541e576a3'); - expect(requestContent.adUnits[0]).to.have.property('auctionId').and.to.equal('ed21b528-bcab-47e2-8605-ec9b71000c89'); - expect(requestContent.adUnits[0]).to.have.property('mediatypes').exist; - expect(requestContent.adUnits[0].mediatypes).to.have.property('video').exist; + it('We perform a test with a video adunit', function() { + const videoBids = [sampleBids[0]]; + videoBids[0].mediaTypes = { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6], + playbackmethod: [2], + skip: 1 + } + }; + const request = spec.buildRequests(videoBids, bidderRequest); + const requestContent = request.data; + expect(request).to.have.property('method').and.to.equal('POST'); + expect(requestContent.imp[0].video.context).to.be.eql('instream'); + expect(requestContent.imp[0].video.playbackmethod[0]).to.be.eql(2); + }) + }); }); - it('Verify video parse response', function () { - const request = spec.buildRequests(VIDEO_BID_REQUEST, DEFAULT_OPTIONS); - const response = spec.interpretResponse(VIDEO_BID_RESPONSE, request); - expect(response).to.have.lengthOf(1); - const bid = response[0]; - expect(bid.cpm).to.equal(4.5421); - expect(bid.vastUrl).to.equal('https://fast.nexx360.io/cache?uuid=b8e7b2f0-c378-479f-aa4f-4f55d5d7d1d5'); - expect(bid.vastImpUrl).to.equal('https://fast.nexx360.io/track-imp?type=prebid&mediatype=video&ssp=appnexus&tag_id=yqsc1tfj&consent=1&price=4.5421'); - expect(bid.width).to.equal(1); - expect(bid.height).to.equal(1); - expect(bid.creativeId).to.equal('97517771'); - expect(bid.currency).to.equal('EUR'); - expect(bid.netRevenue).to.equal(true); - expect(bid.ttl).to.equal(360); - expect(bid.requestId).to.equal('2c129e8e01859a'); - expect(bid.nexx360).to.exist; - expect(bid.nexx360.ssp).to.equal('appnexus'); + describe('interpretResponse()', function() { + it('banner responses', function() { + const response = { + body: { + 'id': 'a8d3a675-a4ba-4d26-807f-c8f2fad821e0', + 'cur': 'USD', + 'seatbid': [ + { + 'bid': [ + { + 'id': '4427551302944024629', + 'impid': '226175918ebeda', + 'price': 1.5, + 'type': 'banner', + 'adomain': [ + 'http://prebid.org' + ], + 'crid': '98493581', + 'ssp': 'appnexus', + 'h': 600, + 'w': 300, + 'cat': [ + 'IAB3-1' + ], + 'creativeuuid': 'fdddcebc-1edf-489d-880d-1418d8bdc493', + 'adUrl': 'https://fast.nexx360.io/cache?uuid=fdddcebc-1edf-489d-880d-1418d8bdc493', + 'ext': { + 'dsp_id': 'ssp1', + 'buyer_id': 'foo', + 'brand_id': 'bar' + } + } + ], + 'seat': 'appnexus' + } + ], + 'cookies': [] + } + }; + const output = spec.interpretResponse(response); + expect(output[0].bidderCode).to.be.eql('nexx360'); + expect(output[0].adUrl).to.be.eql(response.body.seatbid[0].bid[0].adUrl); + expect(output[0].mediaType).to.be.eql(response.body.seatbid[0].bid[0].type); + expect(output[0].currency).to.be.eql(response.body.cur); + expect(output[0].cpm).to.be.eql(response.body.seatbid[0].bid[0].price); + expect(output[0].meta.networkId).to.be.eql(response.body.seatbid[0].bid[0].ext.dsp_id); + expect(output[0].meta.advertiserId).to.be.eql(response.body.seatbid[0].bid[0].ext.buyer_id); + expect(output[0].meta.brandId).to.be.eql(response.body.seatbid[0].bid[0].ext.brand_id); + }); + it('video responses', function() { + const response = { + body: { + 'id': '33894759-0ea2-41f1-84b3-75132eefedb6', + 'cur': 'USD', + 'seatbid': [ + { + 'bid': [ + { + 'id': '294478680080716675', + 'impid': '2c835a6039e65f', + 'price': 5, + 'type': 'instream', + 'adomain': [ + '' + ], + 'crid': '97517771', + 'ssp': 'appnexus', + 'h': 1, + 'w': 1, + 'vastXml': '\n \n \n prebid.org wrapper\n \n \n \n \n \n ' + } + ], + 'seat': 'appnexus' + } + ], + 'cookies': [] + } + }; + const output = spec.interpretResponse(response); + expect(output[0].bidderCode).to.be.eql('nexx360'); + expect(output[0].vastXml).to.be.eql(response.body.seatbid[0].bid[0].vastXml); + expect(output[0].mediaType).to.be.eql('video'); + expect(output[0].currency).to.be.eql(response.body.cur); + expect(output[0].cpm).to.be.eql(response.body.seatbid[0].bid[0].price); + }); }); - it('Verifies bidder code', function () { - expect(spec.code).to.equal('nexx360'); + describe('interpretResponse()', function() { + it('banner responses', function() { + const response = { + body: { + 'id': 'a8d3a675-a4ba-4d26-807f-c8f2fad821e0', + 'cur': 'USD', + 'seatbid': [ + { + 'bid': [ + { + 'id': '4427551302944024629', + 'impid': '226175918ebeda', + 'price': 1.5, + 'type': 'banner', + 'adomain': [ + 'http://prebid.org' + ], + 'crid': '98493581', + 'ssp': 'appnexus', + 'h': 600, + 'w': 300, + 'cat': [ + 'IAB3-1' + ], + 'creativeuuid': 'fdddcebc-1edf-489d-880d-1418d8bdc493', + 'adUrl': 'https://fast.nexx360.io/cache?uuid=fdddcebc-1edf-489d-880d-1418d8bdc493' + } + ], + 'seat': 'appnexus' + } + ], + 'cookies': [] + } + }; + const output = spec.interpretResponse(response); + expect(output[0].bidderCode).to.be.eql('nexx360'); + expect(output[0].adUrl).to.be.eql(response.body.seatbid[0].bid[0].adUrl); + expect(output[0].mediaType).to.be.eql(response.body.seatbid[0].bid[0].type); + expect(output[0].currency).to.be.eql(response.body.cur); + expect(output[0].cpm).to.be.eql(response.body.seatbid[0].bid[0].price); + }); + it('video responses', function() { + const response = { + body: { + 'id': '33894759-0ea2-41f1-84b3-75132eefedb6', + 'cur': 'USD', + 'seatbid': [ + { + 'bid': [ + { + 'id': '294478680080716675', + 'impid': '2c835a6039e65f', + 'price': 5, + 'type': 'instream', + 'adomain': [ + '' + ], + 'crid': '97517771', + 'ssp': 'appnexus', + 'h': 1, + 'w': 1, + 'vastXml': '\n \n \n prebid.org wrapper\n \n \n \n \n \n ' + } + ], + 'seat': 'appnexus' + } + ], + 'cookies': [] + } + }; + const output = spec.interpretResponse(response); + expect(output[0].bidderCode).to.be.eql('nexx360'); + expect(output[0].vastXml).to.be.eql(response.body.seatbid[0].bid[0].vastXml); + expect(output[0].mediaType).to.be.eql('video'); + expect(output[0].currency).to.be.eql(response.body.cur); + expect(output[0].cpm).to.be.eql(response.body.seatbid[0].bid[0].price); + }); }); - it('Verifies bidder aliases', function () { - expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.equal('revenuemaker'); - }); - it('Verifies if bid request valid', function () { - expect(spec.isBidRequestValid(DISPLAY_BID_REQUEST[0])).to.equal(true); - }); - it('Verifies bid won', function () { - const request = spec.buildRequests(DISPLAY_BID_REQUEST, DEFAULT_OPTIONS); - const response = spec.interpretResponse(DISPLAY_BID_RESPONSE, request); - const won = spec.onBidWon(response[0]); - expect(won).to.equal(true); - }); - it('Verifies user sync without cookie in bid response', function () { - var syncs = spec.getUserSyncs({}, [DISPLAY_BID_RESPONSE], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); - expect(syncs).to.have.lengthOf(0); - }); - it('Verifies user sync with cookies in bid response', function () { - DISPLAY_BID_RESPONSE.body.cookies = [{'type': 'image', 'url': 'http://www.cookie.sync.org/'}]; - var syncs = spec.getUserSyncs({}, [DISPLAY_BID_RESPONSE], DEFAULT_OPTIONS.gdprConsent); - expect(syncs).to.have.lengthOf(1); - expect(syncs[0]).to.have.property('type').and.to.equal('image'); - expect(syncs[0]).to.have.property('url').and.to.equal('http://www.cookie.sync.org/'); - }); - it('Verifies user sync with no bid response', function() { - var syncs = spec.getUserSyncs({}, null, DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); - expect(syncs).to.have.lengthOf(0); - }); - it('Verifies user sync with no bid body response', function() { - var syncs = spec.getUserSyncs({}, [], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); - expect(syncs).to.have.lengthOf(0); - var syncs = spec.getUserSyncs({}, [{}], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); - expect(syncs).to.have.lengthOf(0); + describe('getUserSyncs()', function() { + const response = { body: { cookies: [] } }; + it('Verifies user sync without cookie in bid response', function () { + var syncs = spec.getUserSyncs({}, [response], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.lengthOf(0); + }); + it('Verifies user sync with cookies in bid response', function () { + response.body.cookies = [{'type': 'image', 'url': 'http://www.cookie.sync.org/'}]; + var syncs = spec.getUserSyncs({}, [response], DEFAULT_OPTIONS.gdprConsent); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0]).to.have.property('type').and.to.equal('image'); + expect(syncs[0]).to.have.property('url').and.to.equal('http://www.cookie.sync.org/'); + }); + it('Verifies user sync with no bid response', function() { + var syncs = spec.getUserSyncs({}, null, DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.lengthOf(0); + }); + it('Verifies user sync with no bid body response', function() { + var syncs = spec.getUserSyncs({}, [], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.lengthOf(0); + var syncs = spec.getUserSyncs({}, [{}], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.lengthOf(0); + }); }); }); From e32be9098829fa6f7a7d6f8dcbe3d8f10e05099c Mon Sep 17 00:00:00 2001 From: fndigrazia Date: Sun, 27 Nov 2022 01:56:49 -0300 Subject: [PATCH 466/569] ePlanning Bid Adapter : fix support for video auction (#9283) * Fix ad vast * fix lint spaces in tests * fix lint spaces in tests * fix lint spaces in tests --- modules/eplanningBidAdapter.js | 8 ++------ test/spec/modules/eplanningBidAdapter_spec.js | 8 ++++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index b60c3571d47..e230858487f 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -128,9 +128,9 @@ export const spec = { advertiserDomains: ad.adom }; } - if (isVastResponse(ad)) { + if (request && request.data && request.data.vv) { bidResponse.vastXml = ad.adm; - bidResponse.mediaTypes = VIDEO; + bidResponse.mediaType = VIDEO; } else { bidResponse.ad = ad.adm; } @@ -504,8 +504,4 @@ function registerAuction(storageID) { return true; } -function isVastResponse(bid) { - return bid.adm.match(/^( Date: Mon, 28 Nov 2022 16:04:09 +0200 Subject: [PATCH 467/569] add smn alias (#9290) --- modules/admixerBidAdapter.js | 2 +- modules/smnBidAdapter.md | 52 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 modules/smnBidAdapter.md diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index 8fcee60f9f9..90031ed7f5d 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -5,7 +5,7 @@ import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'admixer'; -const ALIASES = ['go2net', 'adblender', 'adsyield', 'futureads']; +const ALIASES = ['go2net', 'adblender', 'adsyield', 'futureads', 'smn']; const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx'; export const spec = { code: BIDDER_CODE, diff --git a/modules/smnBidAdapter.md b/modules/smnBidAdapter.md new file mode 100644 index 00000000000..c9cb777e743 --- /dev/null +++ b/modules/smnBidAdapter.md @@ -0,0 +1,52 @@ +# Overview + +Module Name: SMN Bidder Adapter +Module Type: Bidder Adapter +Maintainer: smnoffice.belgrade@gmail.com + +# Description + +Connects to SMN demand source to fetch bids. +Banner and Video formats are supported. +Please use ```smn``` as the bidder code. + +# Test Parameters +``` + var adUnits = [ + { + code: 'desktop-banner-ad-div', + sizes: [[300, 250]], // a display size + bids: [ + { + bidder: "smn", + params: { + zone: '2eb6bd58-865c-47ce-af7f-a918108c3fd2' + } + } + ] + },{ + code: 'mobile-banner-ad-div', + sizes: [[300, 50]], // a mobile size + bids: [ + { + bidder: "smn", + params: { + zone: '62211486-c50b-4356-9f0f-411778d31fcc' + } + } + ] + },{ + code: 'video-ad', + sizes: [[300, 50]], + mediaType: 'video', + bids: [ + { + bidder: "smn", + params: { + zone: 'ebeb1e79-8cb4-4473-b2d0-2e24b7ff47fd' + } + } + ] + }, + ]; +``` From 4ba48bb0d8b594959290934cbf1a2c4256f0f297 Mon Sep 17 00:00:00 2001 From: Gino <53079123+Skylinar@users.noreply.github.com> Date: Mon, 28 Nov 2022 15:36:50 +0100 Subject: [PATCH 468/569] Smartx Bid Adapter: update custom header (#9291) * Add smartclipBidAdapter * smartxBidAdapter.js - removed unused variables, removed debug, added window before the outstream related functions * - made outstream player configurable * remove wrong named files * camelcase * fix * Out-Stream render update to SmartPlay 5.2 * ESlint fix * ESlint fix * ESlint fix * adjust tests, fixes * ESlint * adjusted desired bitrate examples * added bid.meta.advertiserDomains support * bug fix for numeric elementID outstream render * fix renderer url * support for floors module * bugfixes to be openRTB 2.5 compliant * update internal renderer usage * remove unused outstream_function logic * bugfix outstream options for default outstream renderer configuration * [PREB-10] fix empty title not configurable * add pbjs version * testing with outstream 5.3.0 * pbjs version into content.ext * made visibilityThreshold configurable * adjust position of pbjs version * Merge branch 'master' of https://github.com/prebid/Prebid.js into HEAD * update smartclip outstream player version to support outstream 6 release along with necessary config changes * Add support for schain * vacuuming * update custom header x-openrtb-version to 2.5 Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini Co-authored-by: smartclip-adtech <65160328+smartclip-adtech@users.noreply.github.com> --- modules/smartxBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index f8438a35000..452aaafb09b 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -245,7 +245,7 @@ export const spec = { options: { contentType: 'application/json', customHeaders: { - 'x-openrtb-version': '2.3' + 'x-openrtb-version': '2.5' } } }; From 2ac1e70863ba170eb862d06a428bf858fba2515c Mon Sep 17 00:00:00 2001 From: Alexander Pykhteyev Date: Mon, 28 Nov 2022 22:55:58 +0700 Subject: [PATCH 469/569] LimeLight Bid Adapter : add IionAds alias (#9285) * User sync improvements * User sync improvements * Code review fixes * Pass supply chain to limelightDigitalBidAdapter.js * Add alias for iionads Co-authored-by: apykhteyev Co-authored-by: EngineeringProjectLimeLight <45598299+EngineeringProjectLimeLight@users.noreply.github.com> --- modules/limelightDigitalBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/limelightDigitalBidAdapter.js b/modules/limelightDigitalBidAdapter.js index c39fd36e597..87cf6e4fe21 100644 --- a/modules/limelightDigitalBidAdapter.js +++ b/modules/limelightDigitalBidAdapter.js @@ -26,7 +26,7 @@ function isBidResponseValid(bid) { export const spec = { code: BIDDER_CODE, - aliases: ['pll'], + aliases: ['pll', 'iionads'], supportedMediaTypes: [BANNER, VIDEO], /** From a833997b7da47fff9259fcef7b5be760f659c4fc Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 28 Nov 2022 09:20:10 -0700 Subject: [PATCH 470/569] Generic Analytics Adapter: initial release (#9134) * New module: generic analytics adapter * Use special gvlid value instead of `isVendorless` flag for vendorless consent checks * Mark generic analytics as vendorless for gdpr enforcement * Allow analytics adapters to define dynamic gvlids * Add gvlid option * Gdpr enforcement softVendorExceptions --- modules/gdprEnforcement.js | 50 +-- modules/genericAnalyticsAdapter.js | 157 ++++++++++ modules/pubCommonId.js | 3 +- modules/pubProvidedIdSystem.js | 2 + modules/sharedIdSystem.js | 4 +- src/consentHandler.js | 8 + src/storageManager.js | 9 +- test/spec/modules/gdprEnforcement_spec.js | 183 ++++++----- .../modules/genericAnalyticsAdapter_spec.js | 284 ++++++++++++++++++ test/spec/unit/core/storageManager_spec.js | 3 +- 10 files changed, 605 insertions(+), 98 deletions(-) create mode 100644 modules/genericAnalyticsAdapter.js create mode 100644 test/spec/modules/genericAnalyticsAdapter_spec.js diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index b7035c509b6..9553ad0586a 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -2,7 +2,7 @@ * This module gives publishers extra set of features to enforce individual purposes of TCF v2 */ -import {deepAccess, hasDeviceAccess, isArray, logWarn} from '../src/utils.js'; +import {deepAccess, hasDeviceAccess, isArray, logError, logWarn} from '../src/utils.js'; import {config} from '../src/config.js'; import adapterManager, {gdprDataHandler} from '../src/adapterManager.js'; import {find, includes} from '../src/polyfill.js'; @@ -11,13 +11,7 @@ import {getHook} from '../src/hook.js'; import {validateStorageEnforcement} from '../src/storageManager.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; - -// modules for which vendor consent is not needed (see https://github.com/prebid/Prebid.js/issues/8161) -const VENDORLESS_MODULES = new Set([ - 'sharedId', - 'pubCommonId', - 'pubProvidedId', -]); +import {VENDORLESS_GVLID} from '../src/consentHandler.js'; export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement'; @@ -71,7 +65,7 @@ export const internal = { * @param {{string|Object}} - module * @return {number} - GVL ID */ -export function getGvlid(module) { +export function getGvlid(module, ...args) { let gvlid = null; if (module) { // Check user defined GVL Mapping in pbjs.setConfig() @@ -86,7 +80,7 @@ export function getGvlid(module) { return gvlid; } - gvlid = internal.getGvlidForBidAdapter(moduleCode) || internal.getGvlidForUserIdModule(module) || internal.getGvlidForAnalyticsAdapter(moduleCode); + gvlid = internal.getGvlidForBidAdapter(moduleCode) || internal.getGvlidForUserIdModule(module) || internal.getGvlidForAnalyticsAdapter(moduleCode, ...args); } return gvlid; } @@ -120,10 +114,19 @@ function getGvlidForUserIdModule(userIdModule) { /** * Returns GVL ID for an analytics adapter. If an analytics adapter does not have an associated GVL ID, it returns 'null'. * @param {string} code - 'provider' property on the analytics adapter config + * @param {{}} config - analytics configuration object * @return {number} GVL ID */ -function getGvlidForAnalyticsAdapter(code) { - return adapterManager.getAnalyticsAdapter(code) && (adapterManager.getAnalyticsAdapter(code).gvlid || null); +function getGvlidForAnalyticsAdapter(code, config) { + const adapter = adapterManager.getAnalyticsAdapter(code); + return adapter?.gvlid || ((gvlid) => { + if (typeof gvlid !== 'function') return gvlid; + try { + return gvlid.call(adapter.adapter, config); + } catch (e) { + logError(`Error invoking ${code} adapter.gvlid()`, e) + } + })(adapter?.adapter?.gvlid) } export function shouldEnforce(consentData, purpose, name) { @@ -145,20 +148,20 @@ export function shouldEnforce(consentData, purpose, name) { * @param {Object} consentData - gdpr consent data * @param {string=} currentModule - Bidder code of the current module * @param {number=} gvlId - GVL ID for the module - * @param vendorlessModule a predicate function that takes a module name, and returns true if the module does not need vendor consent * @returns {boolean} */ -export function validateRules(rule, consentData, currentModule, gvlId, vendorlessModule = VENDORLESS_MODULES.has.bind(VENDORLESS_MODULES)) { +export function validateRules(rule, consentData, currentModule, gvlId) { const purposeId = TCF2[Object.keys(TCF2).filter(purposeName => TCF2[purposeName].name === rule.purpose)[0]].id; // return 'true' if vendor present in 'vendorExceptions' - if (includes(rule.vendorExceptions || [], currentModule)) { + if ((rule.vendorExceptions || []).includes(currentModule)) { return true; } + const vendorConsentRequred = !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule))) // get data from the consent string const purposeConsent = deepAccess(consentData, `vendorData.purpose.consents.${purposeId}`); - const vendorConsent = deepAccess(consentData, `vendorData.vendor.consents.${gvlId}`); + const vendorConsent = vendorConsentRequred ? deepAccess(consentData, `vendorData.vendor.consents.${gvlId}`) : true; const liTransparency = deepAccess(consentData, `vendorData.purpose.legitimateInterests.${purposeId}`); /* @@ -166,7 +169,7 @@ export function validateRules(rule, consentData, currentModule, gvlId, vendorles or the user has consented. Similar with vendors. */ const purposeAllowed = rule.enforcePurpose === false || purposeConsent === true; - const vendorAllowed = vendorlessModule(currentModule) || rule.enforceVendor === false || vendorConsent === true; + const vendorAllowed = rule.enforceVendor === false || vendorConsent === true; /* Few if any vendors should be declaring Legitimate Interest for Device Access (Purpose 1), but some are claiming @@ -183,19 +186,18 @@ export function validateRules(rule, consentData, currentModule, gvlId, vendorles /** * This hook checks whether module has permission to access device or not. Device access include cookie and local storage * @param {Function} fn reference to original function (used by hook logic) - * @param isVendorless if true, do not require vendor consent (for e.g. core modules) * @param {Number=} gvlid gvlid of the module * @param {string=} moduleName name of the module * @param result */ -export function deviceAccessHook(fn, isVendorless, gvlid, moduleName, result, {validate = validateRules} = {}) { +export function deviceAccessHook(fn, gvlid, moduleName, result, {validate = validateRules} = {}) { result = Object.assign({}, { hasEnforcementHook: true }); if (!hasDeviceAccess()) { logWarn('Device access is disabled by Publisher'); result.valid = false; - } else if (isVendorless && !strictStorageEnforcement) { + } else if (gvlid === VENDORLESS_GVLID && !strictStorageEnforcement) { // for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set result.valid = true; } else { @@ -203,13 +205,13 @@ export function deviceAccessHook(fn, isVendorless, gvlid, moduleName, result, {v if (shouldEnforce(consentData, 1, moduleName)) { const curBidder = config.getCurrentBidder(); // Bidders have a copy of storage object with bidder code binded. Aliases will also pass the same bidder code when invoking storage functions and hence if alias tries to access device we will try to grab the gvl id for alias instead of original bidder - if (curBidder && (curBidder != moduleName) && adapterManager.aliasRegistry[curBidder] === moduleName) { + if (curBidder && (curBidder !== moduleName) && adapterManager.aliasRegistry[curBidder] === moduleName) { gvlid = getGvlid(curBidder); } else { gvlid = getGvlid(moduleName) || gvlid; } const curModule = moduleName || curBidder; - let isAllowed = validate(purpose1Rule, consentData, curModule, gvlid, isVendorless ? () => true : undefined); + let isAllowed = validate(purpose1Rule, consentData, curModule, gvlid,); if (isAllowed) { result.valid = true; } else { @@ -221,7 +223,7 @@ export function deviceAccessHook(fn, isVendorless, gvlid, moduleName, result, {v result.valid = true; } } - fn.call(this, isVendorless, gvlid, moduleName, result); + fn.call(this, gvlid, moduleName, result); } /** @@ -314,7 +316,7 @@ export function enableAnalyticsHook(fn, config) { } config = config.filter(conf => { const analyticsAdapterCode = conf.provider; - const gvlid = getGvlid(analyticsAdapterCode); + const gvlid = getGvlid(analyticsAdapterCode, conf); const isAllowed = !!validateRules(purpose7Rule, consentData, analyticsAdapterCode, gvlid); if (!isAllowed) { analyticsBlocked.push(analyticsAdapterCode); diff --git a/modules/genericAnalyticsAdapter.js b/modules/genericAnalyticsAdapter.js new file mode 100644 index 00000000000..4909745e72b --- /dev/null +++ b/modules/genericAnalyticsAdapter.js @@ -0,0 +1,157 @@ +import AnalyticsAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import {prefixLog, isPlainObject} from '../src/utils.js'; +import * as CONSTANTS from '../src/constants.json'; +import adapterManager from '../src/adapterManager.js'; +import {ajaxBuilder} from '../src/ajax.js'; + +const DEFAULTS = { + batchSize: 1, + batchDelay: 100, + method: 'POST' +} + +const TYPES = { + handler: 'function', + batchSize: 'number', + batchDelay: 'number', + gvlid: 'number', +} + +const MAX_CALL_DEPTH = 20; + +export function GenericAnalytics() { + const parent = AnalyticsAdapter({analyticsType: 'endpoint'}); + const {logError, logWarn} = prefixLog('Generic analytics:'); + let batch = []; + let callDepth = 0; + let options, handler, timer, translate; + + function optionsAreValid(options) { + if (!options.url && !options.handler) { + logError('options must specify either `url` or `handler`') + return false; + } + if (options.hasOwnProperty('method') && !['GET', 'POST'].includes(options.method)) { + logError('options.method must be GET or POST'); + return false; + } + for (const [field, type] of Object.entries(TYPES)) { + // eslint-disable-next-line valid-typeof + if (options.hasOwnProperty(field) && typeof options[field] !== type) { + logError(`options.${field} must be a ${type}`); + return false; + } + } + if (options.hasOwnProperty('events')) { + if (!isPlainObject(options.events)) { + logError('options.events must be an object'); + return false; + } + for (const [event, handler] of Object.entries(options.events)) { + if (!CONSTANTS.EVENTS.hasOwnProperty(event)) { + logWarn(`options.events.${event} does not match any known Prebid event`); + if (typeof handler !== 'function') { + logError(`options.events.${event} must be a function`); + return false; + } + } + } + } + return true; + } + + function processBatch() { + const currentBatch = batch; + batch = []; + callDepth++; + try { + // the pub-provided handler may inadvertently cause an infinite chain of events; + // even just logging an exception from it may cause an AUCTION_DEBUG event, that + // gets back to the handler, that throws another exception etc. + // to avoid the issue, put a cap on recursion + if (callDepth === MAX_CALL_DEPTH) { + logError('detected probable infinite recursion, discarding events', currentBatch); + } + if (callDepth >= MAX_CALL_DEPTH) { + return; + } + try { + handler(currentBatch); + } catch (e) { + logError('error executing options.handler', e); + } + } finally { + callDepth--; + } + } + + function translator(eventHandlers) { + if (!eventHandlers) { + return (data) => data; + } + return function ({eventType, args}) { + if (eventHandlers.hasOwnProperty(eventType)) { + try { + return eventHandlers[eventType](args); + } catch (e) { + logError(`error executing options.events.${eventType}`, e); + } + } + } + } + + return Object.assign( + Object.create(parent), + { + gvlid(config) { + return config?.options?.gvlid + }, + enableAnalytics(config) { + if (optionsAreValid(config?.options || {})) { + options = Object.assign({}, DEFAULTS, config.options); + handler = options.handler || defaultHandler(options); + translate = translator(options.events); + parent.enableAnalytics.call(this, config); + } + }, + track(event) { + if (event.eventType === CONSTANTS.EVENTS.AUCTION_INIT && event.args.hasOwnProperty('config')) { + // clean up auctionInit event + // TODO: remove this special case in v8 + delete event.args.config; + } + const datum = translate(event); + if (datum != null) { + batch.push(datum); + if (timer != null) { + clearTimeout(timer); + timer = null; + } + if (batch.length >= options.batchSize) { + processBatch(); + } else { + timer = setTimeout(processBatch, options.batchDelay); + } + } + } + } + ) +} + +export function defaultHandler({url, method, batchSize, ajax = ajaxBuilder()}) { + const callbacks = { + success() {}, + error() {} + } + const extract = batchSize > 1 ? (events) => events : (events) => events[0]; + const serialize = method === 'GET' ? (data) => ({data: JSON.stringify(data)}) : (data) => JSON.stringify(data); + + return function (events) { + ajax(url, callbacks, serialize(extract(events)), {method}) + } +} + +adapterManager.registerAnalyticsAdapter({ + adapter: GenericAnalytics(), + code: 'generic', +}); diff --git a/modules/pubCommonId.js b/modules/pubCommonId.js index 5cadd522af2..1fde8f8db5b 100644 --- a/modules/pubCommonId.js +++ b/modules/pubCommonId.js @@ -9,8 +9,9 @@ import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import { getStorageManager } from '../src/storageManager.js'; import {timedAuctionHook} from '../src/utils/perfMetrics.js'; +import {VENDORLESS_GVLID} from '../src/consentHandler.js'; -const storage = getStorageManager({moduleName: 'pubCommonId'}); +const storage = getStorageManager({moduleName: 'pubCommonId', gvlid: VENDORLESS_GVLID}); const ID_NAME = '_pubcid'; const OPTOUT_NAME = '_pubcid_optout'; diff --git a/modules/pubProvidedIdSystem.js b/modules/pubProvidedIdSystem.js index 669d223c57f..baffd997443 100644 --- a/modules/pubProvidedIdSystem.js +++ b/modules/pubProvidedIdSystem.js @@ -7,6 +7,7 @@ import {submodule} from '../src/hook.js'; import { logInfo, isArray } from '../src/utils.js'; +import {VENDORLESS_GVLID} from '../src/consentHandler.js'; const MODULE_NAME = 'pubProvidedId'; @@ -18,6 +19,7 @@ export const pubProvidedIdSubmodule = { * @type {string} */ name: MODULE_NAME, + gvlid: VENDORLESS_GVLID, /** * decode the stored id value for passing to bid request diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 99a34ae17f4..7562b472047 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -9,8 +9,9 @@ import { parseUrl, buildUrl, triggerPixel, logInfo, hasDeviceAccess, generateUUI import {submodule} from '../src/hook.js'; import { coppaDataHandler } from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; +import {VENDORLESS_GVLID} from '../src/consentHandler.js'; -export const storage = getStorageManager({moduleName: 'pubCommonId'}); +export const storage = getStorageManager({moduleName: 'pubCommonId', gvlid: VENDORLESS_GVLID}); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const OPTOUT_NAME = '_pubcid_optout'; @@ -73,6 +74,7 @@ export const sharedIdSystemSubmodule = { */ name: 'sharedId', aliasName: 'pubCommonId', + gvlid: VENDORLESS_GVLID, /** * decode the stored id value for passing to bid requests diff --git a/src/consentHandler.js b/src/consentHandler.js index 861a9894a2c..01470a4b38c 100644 --- a/src/consentHandler.js +++ b/src/consentHandler.js @@ -1,6 +1,14 @@ import {isStr, timestamp} from './utils.js'; import {defer, GreedyPromise} from './utils/promise.js'; +/** + * Placeholder gvlid for when vendor consent is not required. When this value is used as gvlid, the gdpr + * enforcement module will take it to mean "vendor consent was given". + * + * see https://github.com/prebid/Prebid.js/issues/8161 + */ +export const VENDORLESS_GVLID = Object.freeze({}); + export class ConsentHandler { #enabled; #data; diff --git a/src/storageManager.js b/src/storageManager.js index 4ab224f8d9b..eaf35603c60 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -1,6 +1,7 @@ import {hook} from './hook.js'; import {hasDeviceAccess, checkCookieSupport, logError, logInfo, isPlainObject} from './utils.js'; import {bidderSettings as defaultBidderSettings} from './bidderSettings.js'; +import {VENDORLESS_GVLID} from './consentHandler.js'; const moduleTypeWhiteList = ['core', 'prebid-module']; @@ -33,7 +34,9 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = return storageAllowed == null ? false : storageAllowed; } - const isVendorless = moduleTypeWhiteList.includes(moduleType); + if (moduleTypeWhiteList.includes(moduleType)) { + gvlid = gvlid || VENDORLESS_GVLID; + } function isValid(cb) { if (!isBidderAllowed()) { @@ -45,7 +48,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = let hookDetails = { hasEnforcementHook: false } - validateStorageEnforcement(isVendorless, gvlid, bidderCode || moduleName, hookDetails, function(result) { + validateStorageEnforcement(gvlid, bidderCode || moduleName, hookDetails, function(result) { if (result && result.hasEnforcementHook) { value = cb(result); } else { @@ -296,7 +299,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = /** * This hook validates the storage enforcement if gdprEnforcement module is included */ -export const validateStorageEnforcement = hook('async', function(isVendorless, gvlid, moduleName, hookDetails, callback) { +export const validateStorageEnforcement = hook('async', function(gvlid, moduleName, hookDetails, callback) { callback(hookDetails); }, 'validateStorageEnforcement'); diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index ddd522decab..8d58990bb66 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -20,6 +20,7 @@ import * as events from 'src/events.js'; import 'modules/appnexusBidAdapter.js'; // some tests expect this to be in the adapter registry import 'src/prebid.js' import {hook} from '../../../src/hook.js'; +import {VENDORLESS_GVLID} from '../../../src/consentHandler.js'; describe('gdpr enforcement', function () { let nextFnSpy; @@ -150,13 +151,13 @@ describe('gdpr enforcement', function () { } }); - deviceAccessHook(nextFnSpy, false); + deviceAccessHook(nextFnSpy); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: false } - sinon.assert.calledWith(nextFnSpy, false, undefined, undefined, result); + sinon.assert.calledWith(nextFnSpy, undefined, undefined, result); }); it('should only check for consent for vendor exceptions when enforcePurpose and enforceVendor are false', function () { @@ -178,8 +179,8 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); - deviceAccessHook(nextFnSpy, false, 5, 'rubicon'); + deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, 5, 'rubicon'); expect(logWarnSpy.callCount).to.equal(0); }); @@ -201,8 +202,8 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); - deviceAccessHook(nextFnSpy, false, 3, 'rubicon'); + deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, 3, 'rubicon'); expect(logWarnSpy.callCount).to.equal(1); }); @@ -224,13 +225,13 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, false, 1, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, 1, 'appnexus', result); }); it('should use gvlMapping set by publisher', function() { @@ -255,13 +256,13 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, false, 4, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); config.resetConfig(); }); @@ -290,31 +291,17 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, false, 4, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); config.resetConfig(); curBidderStub.restore(); }); - it(`should mark module as vendorless for rule validation when isVendorless = true and ${STRICT_STORAGE_ENFORCEMENT} is set`, () => { - setEnforcementConfig({ - [STRICT_STORAGE_ENFORCEMENT]: true - }); - let consentData = { - vendorData: staticConfig.consentData.getTCData, - gdprApplies: true - } - gdprDataHandlerStub.returns(consentData); - const validate = sinon.stub().callsFake(() => true); - deviceAccessHook(nextFnSpy, true, 123, 'mockModule', undefined, {validate}); - expect(validate.args[0][4]('mockModule')).to.be.true; - }); - it(`should not enforce consent for vendorless modules if ${STRICT_STORAGE_ENFORCEMENT} is not set`, () => { setEnforcementConfig({}); let consentData = { @@ -323,9 +310,9 @@ describe('gdpr enforcement', function () { } gdprDataHandlerStub.returns(consentData); const validate = sinon.stub().callsFake(() => false); - deviceAccessHook(nextFnSpy, true, 123, 'mockModule', undefined, {validate}); + deviceAccessHook(nextFnSpy, VENDORLESS_GVLID, 'mockModule', undefined, {validate}); sinon.assert.callCount(validate, 0); - sinon.assert.calledWith(nextFnSpy, true, 123, 'mockModule', {hasEnforcementHook: true, valid: true}); + sinon.assert.calledWith(nextFnSpy, VENDORLESS_GVLID, 'mockModule', {hasEnforcementHook: true, valid: true}); }) }); @@ -804,11 +791,12 @@ describe('gdpr enforcement', function () { }); describe('validateRules', function () { - const createGdprRule = (purposeName = 'storage', enforcePurpose = true, enforceVendor = true, vendorExceptions = []) => ({ + const createGdprRule = (purposeName = 'storage', enforcePurpose = true, enforceVendor = true, vendorExceptions = [], softVendorExceptions = []) => ({ purpose: purposeName, - enforcePurpose: enforcePurpose, - enforceVendor: enforceVendor, - vendorExceptions: vendorExceptions + enforcePurpose, + enforceVendor, + vendorExceptions, + softVendorExceptions, }); const consentData = { @@ -922,6 +910,19 @@ describe('gdpr enforcement', function () { expect(isAllowed).to.equal(true); }); + describe('when the vendor has a softVendorException', () => { + const gdprRule = createGdprRule('storage', true, true, [], [vendorBlockedModule]); + + it('should return false if general consent was not given', () => { + const isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.be.false; + }) + it('should return true if general consent was given', () => { + const isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.be.true; + }) + }) + describe('when module does not need vendor consent', () => { Object.entries({ 'storage': 1, @@ -937,11 +938,9 @@ describe('gdpr enforcement', function () { it(`should be ${t} when purpose is ${t}`, () => { const consent = utils.deepClone(consentData); consent.vendorData.purpose.consents[purposeNo] = consentGiven; - // vendor consent (and gvlid) should be ignored - consent.vendorData.vendor.consents[123] = !consentGiven; // take legitimate interest out of the picture for this test consent.vendorData.purpose.legitimateInterests = {}; - const actual = validateRules(rule, consent, 'mockModule', 123, () => true); + const actual = validateRules(rule, consent, 'mockModule', VENDORLESS_GVLID); expect(actual).to.equal(consentGiven); }) }) @@ -1131,56 +1130,104 @@ describe('gdpr enforcement', function () { }) }); - describe('getGvlid', function() { + describe('gvlid resolution', () => { let sandbox; - let getGvlidForBidAdapterStub; - let getGvlidForUserIdModuleStub; - let getGvlidForAnalyticsAdapterStub; beforeEach(function() { sandbox = sinon.createSandbox(); - getGvlidForBidAdapterStub = sandbox.stub(internal, 'getGvlidForBidAdapter'); - getGvlidForUserIdModuleStub = sandbox.stub(internal, 'getGvlidForUserIdModule'); - getGvlidForAnalyticsAdapterStub = sandbox.stub(internal, 'getGvlidForAnalyticsAdapter'); }); + afterEach(function() { sandbox.restore(); config.resetConfig(); }); - it('should return "null" if called without passing any argument', function() { - const gvlid = getGvlid(); - expect(gvlid).to.equal(null); - }); + describe('getGvlid', function() { + let getGvlidForBidAdapterStub; + let getGvlidForUserIdModuleStub; + let getGvlidForAnalyticsAdapterStub; + beforeEach(function() { + getGvlidForBidAdapterStub = sandbox.stub(internal, 'getGvlidForBidAdapter'); + getGvlidForUserIdModuleStub = sandbox.stub(internal, 'getGvlidForUserIdModule'); + getGvlidForAnalyticsAdapterStub = sandbox.stub(internal, 'getGvlidForAnalyticsAdapter'); + }); - it('should return "null" if GVL ID is not defined for any of these modules: Bid adapter, UserId submodule and Analytics adapter', function() { - getGvlidForBidAdapterStub.withArgs('moduleA').returns(null); - getGvlidForUserIdModuleStub.withArgs('moduleA').returns(null); - getGvlidForAnalyticsAdapterStub.withArgs('moduleA').returns(null); + it('should return "null" if called without passing any argument', function() { + const gvlid = getGvlid(); + expect(gvlid).to.equal(null); + }); - const gvlid = getGvlid('moduleA'); - expect(gvlid).to.equal(null); - }); + it('should return "null" if GVL ID is not defined for any of these modules: Bid adapter, UserId submodule and Analytics adapter', function() { + getGvlidForBidAdapterStub.withArgs('moduleA').returns(null); + getGvlidForUserIdModuleStub.withArgs('moduleA').returns(null); + getGvlidForAnalyticsAdapterStub.withArgs('moduleA').returns(null); - it('should return the GVL ID from gvlMapping if it is defined in setConfig', function() { - config.setConfig({ - gvlMapping: { - moduleA: 1 - } + const gvlid = getGvlid('moduleA'); + expect(gvlid).to.equal(null); + }); + + it('should return the GVL ID from gvlMapping if it is defined in setConfig', function() { + config.setConfig({ + gvlMapping: { + moduleA: 1 + } + }); + + // Actual GVL ID for moduleA is 2, as defined on its the bidAdapter.js file. + getGvlidForBidAdapterStub.withArgs('moduleA').returns(2); + + const gvlid = getGvlid('moduleA'); + expect(gvlid).to.equal(1); }); - // Actual GVL ID for moduleA is 2, as defined on its the bidAdapter.js file. - getGvlidForBidAdapterStub.withArgs('moduleA').returns(2); + it('should return the GVL ID by calling getGvlidForBidAdapter -> getGvlidForUserIdModule -> getGvlidForAnalyticsAdapter in sequence', function() { + getGvlidForBidAdapterStub.withArgs('moduleA').returns(null); + getGvlidForUserIdModuleStub.withArgs('moduleA').returns(null); + getGvlidForAnalyticsAdapterStub.withArgs('moduleA').returns(7); + + expect(getGvlid('moduleA')).to.equal(7); + }); - const gvlid = getGvlid('moduleA'); - expect(gvlid).to.equal(1); + it('should pass extra arguments to analytics\' getGvlid', () => { + getGvlidForAnalyticsAdapterStub.withArgs('analytics').returns(321); + const cfg = {some: 'args'}; + getGvlid('analytics', cfg); + sinon.assert.calledWith(getGvlidForAnalyticsAdapterStub, 'analytics', cfg); + }); }); - it('should return the GVL ID by calling getGvlidForBidAdapter -> getGvlidForUserIdModule -> getGvlidForAnalyticsAdapter in sequence', function() { - getGvlidForBidAdapterStub.withArgs('moduleA').returns(null); - getGvlidForUserIdModuleStub.withArgs('moduleA').returns(null); - getGvlidForAnalyticsAdapterStub.withArgs('moduleA').returns(7); + describe('getGvlidForAnalyticsAdapter', () => { + let getAnalyticsAdapter, adapter, adapterEntry; + + beforeEach(() => { + adapter = {}; + adapterEntry = { + adapter + }; + getAnalyticsAdapter = sandbox.stub(adapterManager, 'getAnalyticsAdapter'); + getAnalyticsAdapter.withArgs('analytics').returns(adapterEntry); + }); + + it('should return gvlid from adapterManager if defined', () => { + adapterEntry.gvlid = 123; + adapter.gvlid = 321 + expect(internal.getGvlidForAnalyticsAdapter('analytics')).to.equal(123); + }); + + it('should return gvlid from adapter if defined', () => { + adapter.gvlid = 321; + expect(internal.getGvlidForAnalyticsAdapter('analytics')).to.equal(321); + }); - expect(getGvlid('moduleA')).to.equal(7); + it('should invoke adapter.gvlid if it\'s a function', () => { + adapter.gvlid = (cfg) => cfg.k + const cfg = {k: 231}; + expect(internal.getGvlidForAnalyticsAdapter('analytics', cfg)).to.eql(231); + }); + + it('should not choke if adapter gvlid fn throws', () => { + adapter.gvlid = () => { throw new Error(); }; + expect(internal.getGvlidForAnalyticsAdapter('analytics')).to.not.be.ok; + }); }); - }); + }) }); diff --git a/test/spec/modules/genericAnalyticsAdapter_spec.js b/test/spec/modules/genericAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..a5a6074c425 --- /dev/null +++ b/test/spec/modules/genericAnalyticsAdapter_spec.js @@ -0,0 +1,284 @@ +import {defaultHandler, GenericAnalytics} from '../../../modules/genericAnalyticsAdapter.js'; +import * as events from 'src/events.js'; +import * as CONSTANTS from 'src/constants.json'; + +const {AUCTION_INIT, BID_RESPONSE} = CONSTANTS.EVENTS; + +describe('Generic analytics', () => { + describe('adapter', () => { + let adapter, sandbox, clock; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + sandbox.stub(events, 'getEvents').returns([]); + clock = sandbox.useFakeTimers(); + adapter = new GenericAnalytics(); + }); + + afterEach(() => { + adapter.disableAnalytics(); + sandbox.restore(); + }); + + describe('configuration', () => { + it('should be accepted if valid', () => { + adapter.enableAnalytics({ + options: { + url: 'mock', + method: 'GET', + batchSize: 123, + batchDelay: 321 + } + }); + expect(adapter.enabled).to.be.true; + }); + + describe('should not work if', () => { + afterEach(function() { + expect(adapter.enabled).to.equal(false, this.currentTest.title); + }); + + it('neither handler nor url are specified', () => { + adapter.enableAnalytics({}); + }); + + ['batchSize', 'batchDelay', 'handler'].forEach(option => { + it(`${option} is not valid`, () => { + adapter.enableAnalytics({ + options: { + url: 'mock', + [option]: false + } + }); + }); + }); + + it('method is not GET or POST', () => { + adapter.enableAnalytics({ + options: { + url: 'mock', + method: 'PATCH' + } + }); + }); + + it('events is not an object', () => { + adapter.enableAnalytics({ + options: { + url: 'mock', + events: null + } + }); + }); + + it('events\' properties are not functions', () => { + adapter.enableAnalytics({ + options: { + url: 'mock', + events: { + bidResponse: null + } + } + }); + }); + }); + }); + + describe('when handler is specified', () => { + let handler; + beforeEach(() => { + handler = sinon.stub(); + }); + + it('should collect events in batches, and call handler', () => { + adapter.enableAnalytics({ + options: { + handler, + batchSize: 2 + } + }); + events.emit(AUCTION_INIT, {i: 0}); + sinon.assert.notCalled(handler); + events.emit(BID_RESPONSE, {i: 0}); + sinon.assert.calledWith(handler, sinon.match((arg) => { + return sinon.match({eventType: AUCTION_INIT, args: {i: 0}}).test(arg[0]) && + sinon.match({eventType: BID_RESPONSE, args: {i: 0}}).test(arg[1]); + })); + }); + + it('should not choke if handler throws', () => { + adapter.enableAnalytics({ + options: { + handler, + batchSize: 1 + } + }); + handler.throws(new Error()); + events.emit(AUCTION_INIT, {i: 0}); + let recv; + handler.reset(); + handler.callsFake((arg) => { + recv = arg; + }); + events.emit(BID_RESPONSE, {i: 1}); + expect(recv).to.eql([{eventType: BID_RESPONSE, args: {i: 1}}]); + }); + + it('should not cause infinite recursion, if handler triggers more events', () => { + let i = 0; + handler.callsFake(() => { + if (i <= 100) { + i++; + events.emit(BID_RESPONSE, {}); + } + }); + adapter.enableAnalytics({ + options: { + handler, + } + }); + events.emit(AUCTION_INIT, {}); + expect(i >= 100).to.be.false; + }); + + it('should send incomplete batch after batchDelay', () => { + adapter.enableAnalytics({ + options: { + batchDelay: 100, + batchSize: 2, + handler + } + }); + [0, 1, 2].forEach(i => events.emit(BID_RESPONSE, {i})); + sinon.assert.calledOnce(handler); + clock.tick(100); + sinon.assert.calledTwice(handler); + }); + + it('does not send empty batches', () => { + adapter.enableAnalytics({ + options: { + batchDelay: 100, + batchSize: 2, + handler + } + }); + [0, 1, 2].forEach(i => events.emit(BID_RESPONSE, {i})); + sinon.assert.calledOnce(handler); + clock.tick(50); + events.emit(BID_RESPONSE, {i: 3}); + sinon.assert.calledTwice(handler); + clock.tick(100); + sinon.assert.calledTwice(handler); + }); + + describe('and options.events is specified', () => { + it('filters out other events', () => { + adapter.enableAnalytics({ + options: { + handler, + events: { + bidResponse(bid) { + return bid; + } + } + } + }); + events.emit(AUCTION_INIT, {}); + sinon.assert.notCalled(handler); + }); + + it('transforms event data', () => { + adapter.enableAnalytics({ + options: { + handler, + events: { + bidResponse(bid) { + return { + extra: 'data', + prop: bid.prop + } + } + } + } + }); + events.emit(BID_RESPONSE, {prop: 'value', i: 0}); + sinon.assert.calledWith(handler, sinon.match(data => sinon.match({extra: 'data', prop: 'value'}).test(data[0]))); + }); + + it('does not choke if an event handler throws', () => { + adapter.enableAnalytics({ + options: { + handler, + events: { + bidResponse(bid) { + return bid; + }, + auctionInit(auction) { + throw new Error(); + } + } + } + }); + events.emit(AUCTION_INIT, {}); + events.emit(BID_RESPONSE, {i: 0}); + sinon.assert.calledOnce(handler); + sinon.assert.calledWith(handler, sinon.match(data => sinon.match({i: 0}).test(data[0]))); + }); + + it('filters out events when their handler returns undefined', () => { + adapter.enableAnalytics({ + options: { + handler, + events: { + auctionInit(auction) { + return auction; + }, + bidResponse(bid) {} + } + } + }); + events.emit(AUCTION_INIT, {i: 0}); + events.emit(BID_RESPONSE, {i: 1}); + sinon.assert.calledOnce(handler); + sinon.assert.calledWith(handler, sinon.match(data => sinon.match({i: 0}).test(data[0]))); + }); + }); + }); + }); + + describe('default handler', () => { + const url = 'mock-url'; + + let ajax; + beforeEach(() => { + ajax = sinon.stub(); + }); + + Object.entries({ + 'GET': (data) => JSON.parse(data.data), + 'POST': (data) => JSON.parse(data) + }).forEach(([method, parse]) => { + describe(`when HTTP method is ${method}`, () => { + it('should send single event when batchSize is 1', () => { + const handler = defaultHandler({url, method, batchSize: 1, ajax}); + const payload = {i: 0}; + handler([payload, {}]); + sinon.assert.calledWith(ajax, url, sinon.match.any, + sinon.match(data => sinon.match(payload).test(parse(data))), + {method} + ); + }); + + it('should send multiple events when batchSize is greater than 1', () => { + const handler = defaultHandler({url, method, batchSize: 10, ajax}); + const payload = [{i: 0}, {i: 1}]; + handler(payload); + sinon.assert.calledWith(ajax, url, sinon.match.any, + sinon.match(data => sinon.match(payload).test(parse(data))), + {method} + ); + }); + }); + }); + }); +}); diff --git a/test/spec/unit/core/storageManager_spec.js b/test/spec/unit/core/storageManager_spec.js index cb38aed9e47..58bf8c9eb25 100644 --- a/test/spec/unit/core/storageManager_spec.js +++ b/test/spec/unit/core/storageManager_spec.js @@ -8,6 +8,7 @@ import { import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import {hook} from '../../../../src/hook.js'; +import {VENDORLESS_GVLID} from '../../../../src/consentHandler.js'; describe('storage manager', function() { before(() => { @@ -73,7 +74,7 @@ describe('storage manager', function() { it('should respect (vendorless) consent enforcement', () => { storage.localStorageIsEnabled(); - expect(validateHook.args[0][1]).to.eql(true); // isVendorless should be set to true + expect(validateHook.args[0][1]).to.equal(VENDORLESS_GVLID); // gvlid should be set to VENDORLESS_GVLID }); it('should respect the deviceAccess flag', () => { From b0413b2225801317d0eb20040fc57c1cc25c9eaa Mon Sep 17 00:00:00 2001 From: southern-growthcode <79725079+southern-growthcode@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:22:00 -0500 Subject: [PATCH 471/569] GrowthCode Analytics Adaptor Module: initial module release (#9021) * Initial check-in ofthe GrowthCode Adaptor * Growthcode ID System * Working on test module * Tests for the growthCode Id System * Clean up tests for GrowthCode * Fixed the default values for shareID * New Analyics package * Growthcode Analyics Adapter * Backout growthcode User ID module --- integrationExamples/gpt/growthcode.html | 134 +++++++++++++ modules/growthCodeAnalyticsAdapter.js | 176 ++++++++++++++++++ modules/growthCodeAnalyticsAdapter.md | 41 ++++ .../growthCodeAnalyticsAdapter_spec.js | 70 +++++++ 4 files changed, 421 insertions(+) create mode 100644 integrationExamples/gpt/growthcode.html create mode 100644 modules/growthCodeAnalyticsAdapter.js create mode 100644 modules/growthCodeAnalyticsAdapter.md create mode 100644 test/spec/modules/growthCodeAnalyticsAdapter_spec.js diff --git a/integrationExamples/gpt/growthcode.html b/integrationExamples/gpt/growthcode.html new file mode 100644 index 00000000000..ede51d2d869 --- /dev/null +++ b/integrationExamples/gpt/growthcode.html @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + diff --git a/modules/growthCodeAnalyticsAdapter.js b/modules/growthCodeAnalyticsAdapter.js new file mode 100644 index 00000000000..1f11b891139 --- /dev/null +++ b/modules/growthCodeAnalyticsAdapter.js @@ -0,0 +1,176 @@ +/** + * growthCodeAnalyticsAdapter.js - GrowthCode Analytics Adapter + */ +import { ajax } from '../src/ajax.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; +import * as utils from '../src/utils.js'; +import CONSTANTS from '../src/constants.json'; +import { getStorageManager } from '../src/storageManager.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {logError, logInfo} from '../src/utils.js'; + +const MODULE_NAME = 'growthCodeAnalytics'; +const DEFAULT_PID = 'INVALID_PID' +const ENDPOINT_URL = 'https://p2.gcprivacy.com/v1/pb/analytics' + +export const storage = getStorageManager(); + +let sessionId = utils.generateUUID(); + +let trackEvents = []; +let pid = DEFAULT_PID; +let url = ENDPOINT_URL; + +let eventQueue = []; + +let startAuction = 0; +let bidRequestTimeout = 0; +let analyticsType = 'endpoint'; + +let growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsType}), { + track({eventType, eventData}) { + eventData = eventData ? JSON.parse(JSON.stringify(eventData)) : {}; + let data = {}; + if (!trackEvents.includes(eventType)) return; + switch (eventType) { + case CONSTANTS.EVENTS.AUCTION_INIT: { + data = eventData; + startAuction = data.timestamp; + bidRequestTimeout = data.timeout; + break; + } + + case CONSTANTS.EVENTS.AUCTION_END: { + data = eventData; + data.start = startAuction; + data.end = Date.now(); + break; + } + + case CONSTANTS.EVENTS.BID_ADJUSTMENT: { + data.bidders = eventData; + break; + } + + case CONSTANTS.EVENTS.BID_TIMEOUT: { + data.bidders = eventData; + data.duration = bidRequestTimeout; + break; + } + + case CONSTANTS.EVENTS.BID_REQUESTED: { + data = eventData; + break; + } + + case CONSTANTS.EVENTS.BID_RESPONSE: { + data = eventData; + delete data.ad; + break; + } + + case CONSTANTS.EVENTS.BID_WON: { + data = eventData; + delete data.ad; + delete data.adUrl; + break; + } + + case CONSTANTS.EVENTS.BIDDER_DONE: { + data = eventData; + break; + } + + case CONSTANTS.EVENTS.SET_TARGETING: { + data.targetings = eventData; + break; + } + + case CONSTANTS.EVENTS.REQUEST_BIDS: { + data = eventData; + break; + } + + case CONSTANTS.EVENTS.ADD_AD_UNITS: { + data = eventData; + break; + } + + default: + return; + } + + data.eventType = eventType; + data.timestamp = data.timestamp || Date.now(); + + sendEvent(data); + } +}); + +growthCodeAnalyticsAdapter.originEnableAnalytics = growthCodeAnalyticsAdapter.enableAnalytics; + +growthCodeAnalyticsAdapter.enableAnalytics = function(conf = {}) { + if (typeof conf.options === 'object') { + if (conf.options.pid) { + pid = conf.options.pid; + url = conf.options.url ? conf.options.url : ENDPOINT_URL; + } else { + logError(MODULE_NAME + ' Not a valid PartnerID') + return + } + if (conf.options.trackEvents) { + trackEvents = conf.options.trackEvents; + } + } else { + logError(MODULE_NAME + ' Invalid configuration'); + return; + } + + growthCodeAnalyticsAdapter.originEnableAnalytics(conf); +}; + +function logToServer() { + if (pid === DEFAULT_PID) return; + if (eventQueue.length > 1) { + let data = { + session: sessionId, + pid: pid, + timestamp: Date.now(), + timezoneoffset: new Date().getTimezoneOffset(), + url: getRefererInfo().page, + referer: document.referrer, + events: eventQueue + }; + + ajax(url, { + success: response => { + logInfo(MODULE_NAME + ' Send Data to Server') + }, + error: error => { + logInfo(MODULE_NAME + ' Problem Send Data to Server: ' + error) + } + }, JSON.stringify(data), {method: 'POST', withCredentials: true}) + + eventQueue = [ + ]; + } +} + +function sendEvent(event) { + eventQueue.push(event); + logInfo(MODULE_NAME + 'Analytics Event: ' + event); + + if (event.eventType === CONSTANTS.EVENTS.AUCTION_END) { + logToServer(); + } +} + +adapterManager.registerAnalyticsAdapter({ + adapter: growthCodeAnalyticsAdapter, + code: 'growthCodeAnalytics' +}); + +growthCodeAnalyticsAdapter.logToServer = logToServer; + +export default growthCodeAnalyticsAdapter; diff --git a/modules/growthCodeAnalyticsAdapter.md b/modules/growthCodeAnalyticsAdapter.md new file mode 100644 index 00000000000..e45cb2e9c62 --- /dev/null +++ b/modules/growthCodeAnalyticsAdapter.md @@ -0,0 +1,41 @@ +## GrowthCode Analytics Adapter + +[GrowthCode](https://growthcode.io) offers scaled infrastructure-as-a-service to +empower independent publishers to harness data and take control of identity and +audience while rapidly aligning to industry changes and margin pressure. + +## Building Prebid with GrowthCode Support + +First, make sure to add the GrowthCode submodule to your Prebid.js package with: + +``` +gulp build --modules=growthCodeIdSystem,growthCodeAnalyticsAdapter,userId +``` + +The following configuration parameters are available: + +```javascript +pbjs.enableAnalytics({ + provider: 'growthCodeAnalytics', + options: { + pid: '', + trackEvents: [ + 'auctionEnd', + 'bidAdjustment', + 'bidTimeout', + 'bidRequested', + 'bidResponse', + 'noBid', + 'bidWon', + 'bidderDone'] + } +}); +``` + +| Param enableAnalytics | Scope | Type | Description | Example | +|-----------------------|----------|--------|-------------------------------------------------------------|--------------------------| +| provider | Required | String | The name of this Adapter. | `"growthCodeAnalytics"` | +| params | Required | Object | Details of module params. | | +| params.pid | Required | String | This is the Customer ID value obtained via Intimate Merger. | `""` | +| params.url | Optional | String | Custom URL for server | | +| params.trackEvents | Required | String | Name if the variable that holds your publisher ID | | diff --git a/test/spec/modules/growthCodeAnalyticsAdapter_spec.js b/test/spec/modules/growthCodeAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..e542a2641e8 --- /dev/null +++ b/test/spec/modules/growthCodeAnalyticsAdapter_spec.js @@ -0,0 +1,70 @@ +import adapterManager from '../../../src/adapterManager.js'; +import growthCodeAnalyticsAdapter from '../../../modules/growthCodeAnalyticsAdapter.js'; +import { expect } from 'chai'; +import * as events from '../../../src/events.js'; +import constants from '../../../src/constants.json'; +import { generateUUID } from '../../../src/utils.js'; +import { server } from 'test/mocks/xhr.js'; + +describe('growthCode analytics adapter', () => { + beforeEach(() => { + growthCodeAnalyticsAdapter.enableAnalytics({ + provider: 'growthCodeAnalytics', + options: { + pid: 'TEST01', + trackEvents: [ + 'auctionInit', + 'auctionEnd', + 'bidAdjustment', + 'bidTimeout', + 'bidTimeout', + 'bidRequested', + 'bidResponse', + 'setTargeting', + 'requestBids', + 'addAdUnits', + 'noBid', + 'bidWon', + 'bidderDone'] + } + }); + }); + + afterEach(() => { + growthCodeAnalyticsAdapter.disableAnalytics(); + }); + + it('registers itself with the adapter manager', () => { + const adapter = adapterManager.getAnalyticsAdapter('growthCodeAnalytics'); + expect(adapter).to.exist; + expect(adapter.adapter).to.equal(growthCodeAnalyticsAdapter); + }); + + it('tolerates undefined or empty config', () => { + growthCodeAnalyticsAdapter.enableAnalytics(undefined); + growthCodeAnalyticsAdapter.enableAnalytics({}); + }); + + it('sends auction end events to the backend', () => { + const auction = { + auctionId: generateUUID(), + adUnits: [{ + code: 'usr1234', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600], [728, 90]] + } + }, + adUnitCodes: ['usr1234'] + }], + }; + events.emit(constants.EVENTS.AUCTION_END, auction); + assert(server.requests.length > 0) + const body = JSON.parse(server.requests[0].requestBody); + var eventTypes = []; + body.events.forEach(e => eventTypes.push(e.eventType)); + assert(eventTypes.length > 0) + assert(eventTypes.indexOf(constants.EVENTS.AUCTION_END) > -1); + growthCodeAnalyticsAdapter.disableAnalytics(); + }); +}); From b87ebac3d855c08312f579e53180069fe2469695 Mon Sep 17 00:00:00 2001 From: yieldlift <61510052+yieldlift@users.noreply.github.com> Date: Mon, 28 Nov 2022 17:49:00 +0100 Subject: [PATCH 472/569] Yieldlift Bid Adapter: update ttl (#9232) * Updating TTL, changing endpoint * Test fixed Co-authored-by: Danijel Predarski --- modules/yieldliftBidAdapter.js | 6 +++--- test/spec/modules/yieldliftBidAdapter_spec.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/yieldliftBidAdapter.js b/modules/yieldliftBidAdapter.js index 160d7e9a009..6e4bce15187 100644 --- a/modules/yieldliftBidAdapter.js +++ b/modules/yieldliftBidAdapter.js @@ -2,9 +2,9 @@ import {deepSetValue, logInfo, deepAccess} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -const ENDPOINT_URL = 'https://x.yieldlift.com/auction'; +const ENDPOINT_URL = 'https://x.yieldlift.com/pbjs'; -const DEFAULT_BID_TTL = 30; +const DEFAULT_BID_TTL = 300; const DEFAULT_CURRENCY = 'USD'; const DEFAULT_NET_REVENUE = true; @@ -98,7 +98,7 @@ export const spec = { width: bid.w, height: bid.h, ad: bid.adm, - ttl: DEFAULT_BID_TTL, + ttl: typeof bid.exp === 'number' ? bid.exp : DEFAULT_BID_TTL, creativeId: bid.crid, netRevenue: DEFAULT_NET_REVENUE, currency: DEFAULT_CURRENCY, diff --git a/test/spec/modules/yieldliftBidAdapter_spec.js b/test/spec/modules/yieldliftBidAdapter_spec.js index c2379ed7778..0cabdb594fe 100644 --- a/test/spec/modules/yieldliftBidAdapter_spec.js +++ b/test/spec/modules/yieldliftBidAdapter_spec.js @@ -229,7 +229,7 @@ describe('YieldLift', function () { expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm); expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid); expect(bids[index].meta).to.have.property('advertiserDomains', RESPONSE.body.seatbid[0].bid[index].advertiserDomains); - expect(bids[index]).to.have.property('ttl', 30); + expect(bids[index]).to.have.property('ttl', 300); expect(bids[index]).to.have.property('netRevenue', true); } }); From 638691fe83a354631f0ec866095e8df02a89ab8a Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 28 Nov 2022 09:52:31 -0700 Subject: [PATCH 473/569] Multiple analytics modules: allow pub-defined event filters; do not block auction for analytics (#9113) * Multiple analytics modules: allow pub-defined event filters for analytics * AnalyticsAdapter: make event handling non-blocking * Fix infinite recursion on nested events * Exclude AUCTION_DEBUG by default * Use a default whitelist intead of default blacklist * Try to detect and break out of infinite loops caused by events triggering other events --- .../analyticsAdapter/AnalyticsAdapter.js | 197 +++++++-------- src/events.js | 4 + test/helpers/analytics.js | 34 +++ test/mocks/analyticsStub.js | 4 +- test/spec/AnalyticsAdapter_spec.js | 235 +++++++++--------- .../modules/adWMGAnalyticsAdapter_spec.js | 18 +- .../modules/adagioAnalyticsAdapter_spec.js | 43 ++-- .../modules/bidwatchAnalyticsAdapter_spec.js | 2 - .../modules/concertAnalyticsAdapter_spec.js | 6 +- .../modules/eplanningAnalyticsAdapter_spec.js | 3 - .../modules/invisiblyAnalyticsAdapter_spec.js | 44 ++-- .../modules/konduitAnalyticsAdapter_spec.js | 2 - .../liveIntentAnalyticsAdapter_spec.js | 9 +- .../modules/medianetAnalyticsAdapter_spec.js | 8 +- .../modules/optimonAnalyticsAdapter_spec.js | 14 +- .../modules/pianoDmpAnalyticsAdapter_spec.js | 15 +- .../prebidmanagerAnalyticsAdapter_spec.js | 15 +- .../modules/pubperfAnalyticsAdapter_spec.js | 25 +- .../modules/pubstackAnalyticsAdapter_spec.js | 20 +- .../modules/pubwiseAnalyticsAdapter_spec.js | 32 +-- .../modules/sigmoidAnalyticsAdapter_spec.js | 12 +- .../modules/sovrnAnalyticsAdapter_spec.js | 24 +- .../modules/yieldoneAnalyticsAdapter_spec.js | 7 +- 23 files changed, 359 insertions(+), 414 deletions(-) create mode 100644 test/helpers/analytics.js diff --git a/libraries/analyticsAdapter/AnalyticsAdapter.js b/libraries/analyticsAdapter/AnalyticsAdapter.js index 277a1455a75..b6e270b3c3c 100644 --- a/libraries/analyticsAdapter/AnalyticsAdapter.js +++ b/libraries/analyticsAdapter/AnalyticsAdapter.js @@ -1,47 +1,68 @@ import CONSTANTS from '../../src/constants.json'; -import { ajax } from '../../src/ajax.js'; -import { logMessage, _each } from '../../src/utils.js'; -import * as events from '../../src/events.js' +import {ajax} from '../../src/ajax.js'; +import {logError, logMessage} from '../../src/utils.js'; +import * as events from '../../src/events.js'; export const _internal = { ajax }; - -const { - EVENTS: { - AUCTION_INIT, - AUCTION_END, - REQUEST_BIDS, - BID_REQUESTED, - BID_TIMEOUT, - BID_RESPONSE, - NO_BID, - BID_WON, - BID_ADJUSTMENT, - BIDDER_DONE, - SET_TARGETING, - AD_RENDER_FAILED, - AD_RENDER_SUCCEEDED, - AUCTION_DEBUG, - ADD_AD_UNITS, - BILLABLE_EVENT - } -} = CONSTANTS; - const ENDPOINT = 'endpoint'; const BUNDLE = 'bundle'; +export const DEFAULT_INCLUDE_EVENTS = Object.values(CONSTANTS.EVENTS) + .filter(ev => ev !== CONSTANTS.EVENTS.AUCTION_DEBUG); + +let debounceDelay = 100; + +export function setDebounceDelay(delay) { + debounceDelay = delay; +} + export default function AnalyticsAdapter({ url, analyticsType, global, handler }) { - const _queue = []; - let _eventCount = 0; - let _enableCheck = true; - let _handlers; - let _enabled = false; - let _sampled = true; - - if (analyticsType === ENDPOINT || BUNDLE) { - _emptyQueue(); - } + const queue = []; + let handlers; + let enabled = false; + let sampled = true; + let provider; + + const emptyQueue = (() => { + let running = false; + let timer; + const clearQueue = () => { + if (!running) { + running = true; // needed to avoid recursive re-processing when analytics event handlers trigger other events + try { + let i = 0; + let notDecreasing = 0; + while (queue.length > 0) { + i++; + const len = queue.length; + queue.shift()(); + if (queue.length >= len) { + notDecreasing++; + } else { + notDecreasing = 0 + } + if (notDecreasing >= 10) { + logError('Detected probable infinite loop, discarding events', queue) + queue.length = 0; + return; + } + } + logMessage(`${provider} analytics: processed ${i} events`); + } finally { + running = false; + } + } + }; + return function () { + if (timer != null) { + clearTimeout(timer); + timer = null; + } + debounceDelay === 0 ? clearQueue() : timer = setTimeout(clearQueue, debounceDelay); + } + })(); return Object.defineProperties({ track: _track, @@ -54,7 +75,7 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler } getUrl: () => url }, { enabled: { - get: () => _enabled + get: () => enabled } }); @@ -72,69 +93,58 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler } _internal.ajax(url, callback, JSON.stringify({ eventType, args })); } - function _enqueue({ eventType, args }) { - const _this = this; - - if (global && window[global] && eventType && args) { - this.track({ eventType, args }); - } else { - _queue.push(function () { - _eventCount++; - _this.track({ eventType, args }); - }); - } + function _enqueue({eventType, args}) { + queue.push(() => { + this.track({eventType, args}); + }); + emptyQueue(); } function _enable(config) { + provider = config?.provider; var _this = this; if (typeof config === 'object' && typeof config.options === 'object') { - _sampled = typeof config.options.sampling === 'undefined' || Math.random() < parseFloat(config.options.sampling); + sampled = typeof config.options.sampling === 'undefined' || Math.random() < parseFloat(config.options.sampling); } else { - _sampled = true; + sampled = true; } - if (_sampled) { + if (sampled) { + const trackedEvents = (() => { + const {includeEvents = DEFAULT_INCLUDE_EVENTS, excludeEvents = []} = (config || {}); + return new Set( + Object.values(CONSTANTS.EVENTS) + .filter(ev => includeEvents.includes(ev)) + .filter(ev => !excludeEvents.includes(ev)) + ); + })(); + // first send all events fired before enableAnalytics called events.getEvents().forEach(event => { - if (!event) { + if (!event || !trackedEvents.has(event.eventType)) { return; } const { eventType, args } = event; - - if (eventType !== BID_TIMEOUT) { - _enqueue.call(_this, { eventType, args }); - } + _enqueue.call(_this, { eventType, args }); }); // Next register event listeners to send data immediately - - _handlers = { - [REQUEST_BIDS]: args => this.enqueue({ eventType: REQUEST_BIDS, args }), - [BID_REQUESTED]: args => this.enqueue({ eventType: BID_REQUESTED, args }), - [BID_RESPONSE]: args => this.enqueue({ eventType: BID_RESPONSE, args }), - [NO_BID]: args => this.enqueue({ eventType: NO_BID, args }), - [BID_TIMEOUT]: args => this.enqueue({ eventType: BID_TIMEOUT, args }), - [BID_WON]: args => this.enqueue({ eventType: BID_WON, args }), - [BID_ADJUSTMENT]: args => this.enqueue({ eventType: BID_ADJUSTMENT, args }), - [BIDDER_DONE]: args => this.enqueue({ eventType: BIDDER_DONE, args }), - [SET_TARGETING]: args => this.enqueue({ eventType: SET_TARGETING, args }), - [AUCTION_END]: args => this.enqueue({ eventType: AUCTION_END, args }), - [AD_RENDER_FAILED]: args => this.enqueue({ eventType: AD_RENDER_FAILED, args }), - [AD_RENDER_SUCCEEDED]: args => this.enqueue({ eventType: AD_RENDER_SUCCEEDED, args }), - [AUCTION_DEBUG]: args => this.enqueue({ eventType: AUCTION_DEBUG, args }), - [ADD_AD_UNITS]: args => this.enqueue({ eventType: ADD_AD_UNITS, args }), - [BILLABLE_EVENT]: args => this.enqueue({ eventType: BILLABLE_EVENT, args }), - [AUCTION_INIT]: args => { - args.config = typeof config === 'object' ? config.options || {} : {}; // enableAnaltyics configuration object - this.enqueue({ eventType: AUCTION_INIT, args }); - } - }; - - _each(_handlers, (handler, event) => { - events.on(event, handler); - }); + handlers = Object.fromEntries( + Array.from(trackedEvents) + .map((ev) => { + const handler = ev === CONSTANTS.EVENTS.AUCTION_INIT + ? (args) => { + // TODO: remove this special case in v8 + args.config = typeof config === 'object' ? config.options || {} : {}; + this.enqueue({eventType: ev, args}); + } + : (args) => this.enqueue({eventType: ev, args}); + events.on(ev, handler); + return [ev, handler]; + }) + ) } else { logMessage(`Analytics adapter for "${global}" disabled by sampling`); } @@ -144,31 +154,14 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler } this.enableAnalytics = function _enable() { return logMessage(`Analytics adapter for "${global}" already enabled, unnecessary call to \`enableAnalytics\`.`); }; - _enabled = true; + enabled = true; } function _disable() { - _each(_handlers, (handler, event) => { + Object.entries(handlers || {}).forEach(([event, handler]) => { events.off(event, handler); - }); + }) this.enableAnalytics = this._oldEnable ? this._oldEnable : _enable; - _enabled = false; - } - - function _emptyQueue() { - if (_enableCheck) { - for (var i = 0; i < _queue.length; i++) { - _queue[i](); - } - - // override push to execute the command immediately from now on - _queue.push = function (fn) { - fn(); - }; - - _enableCheck = false; - } - - logMessage(`event count sent to ${global}: ${_eventCount}`); + enabled = false; } } diff --git a/src/events.js b/src/events.js index 8e6117c3abf..62f8c070deb 100644 --- a/src/events.js +++ b/src/events.js @@ -157,3 +157,7 @@ const _public = (function () { utils._setEventEmitter(_public.emit.bind(_public)); export const {on, off, get, getEvents, emit, addEvents} = _public; + +export function clearEvents() { + eventsFired.length = 0; +} diff --git a/test/helpers/analytics.js b/test/helpers/analytics.js new file mode 100644 index 00000000000..b376118dc6f --- /dev/null +++ b/test/helpers/analytics.js @@ -0,0 +1,34 @@ +import * as pbEvents from 'src/events.js'; +import constants from '../../src/constants.json'; + +export function fireEvents(events = [ + constants.EVENTS.AUCTION_INIT, + constants.EVENTS.AUCTION_END, + constants.EVENTS.BID_REQUESTED, + constants.EVENTS.BID_RESPONSE, + constants.EVENTS.BID_WON +]) { + return events.map((ev, i) => { + ev = Array.isArray(ev) ? ev : [ev, {i: i}]; + pbEvents.emit.apply(null, ev) + return ev; + }); +} + +export function expectEvents(events) { + events = fireEvents(events); + return { + to: { + beTrackedBy(trackFn) { + events.forEach(([eventType, args]) => { + sinon.assert.calledWithMatch(trackFn, sinon.match({eventType, args})); + }); + }, + beBundledTo(bundleFn) { + events.forEach(([eventType, args]) => { + sinon.assert.calledWithMatch(bundleFn, sinon.match.any, eventType, sinon.match(args)) + }); + }, + }, + }; +} diff --git a/test/mocks/analyticsStub.js b/test/mocks/analyticsStub.js index 8507c5a6275..98e0f56688f 100644 --- a/test/mocks/analyticsStub.js +++ b/test/mocks/analyticsStub.js @@ -1,8 +1,10 @@ -import {_internal} from '../../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import {_internal, setDebounceDelay} from '../../libraries/analyticsAdapter/AnalyticsAdapter.js'; before(() => { // stub out analytics networking to avoid random events polluting the global xhr mock disableAjaxForAnalytics(); + // make analytics event handling synchronous + setDebounceDelay(0); }) export function disableAjaxForAnalytics() { diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index 06ab8838985..62c00e04403 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -1,18 +1,17 @@ -import { expect } from 'chai'; +import {expect} from 'chai'; import * as events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; -import { server } from 'test/mocks/xhr.js'; +import {server} from 'test/mocks/xhr.js'; import {disableAjaxForAnalytics, enableAjaxForAnalytics} from '../mocks/analyticsStub.js'; +import {clearEvents} from 'src/events.js'; +import { + DEFAULT_EXCLUDE_EVENTS, + DEFAULT_INCLUDE_EVENTS, + setDebounceDelay +} from '../../libraries/analyticsAdapter/AnalyticsAdapter.js'; -const REQUEST_BIDS = CONSTANTS.EVENTS.REQUEST_BIDS; -const BID_REQUESTED = CONSTANTS.EVENTS.BID_REQUESTED; -const BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; const BID_WON = CONSTANTS.EVENTS.BID_WON; -const BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; -const AD_RENDER_FAILED = CONSTANTS.EVENTS.AD_RENDER_FAILED; -const AD_RENDER_SUCCEEDED = CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED; -const AUCTION_DEBUG = CONSTANTS.EVENTS.AUCTION_DEBUG; -const ADD_AD_UNITS = CONSTANTS.EVENTS.ADD_AD_UNITS; +const NO_BID = CONSTANTS.EVENTS.NO_BID; const AnalyticsAdapter = require('libraries/analyticsAdapter/AnalyticsAdapter.js').default; const config = { @@ -35,6 +34,7 @@ FEATURE: Analytics Adapters API afterEach(function () { adapter.disableAnalytics(); + clearEvents(); }); it('should track enable status in `enabled`', () => { @@ -43,21 +43,21 @@ FEATURE: Analytics Adapters API expect(adapter.enabled).to.equal(true); adapter.disableAnalytics(); expect(adapter.enabled).to.equal(false); - }) + }); it(`SHOULD call the endpoint WHEN an event occurs that is to be tracked`, function () { - const eventType = BID_REQUESTED; - const args = { some: 'data' }; + const eventType = BID_WON; + const args = {some: 'data'}; - adapter.track({ eventType, args }); + adapter.track({eventType, args}); let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {some: 'data'}, eventType: 'bidRequested'}); + expect(result).to.deep.equal({args: {some: 'data'}, eventType}); }); it(`SHOULD queue the event first and then track it WHEN an event occurs before tracking library is available`, function () { - const eventType = BID_RESPONSE; - const args = { wat: 'wot' }; + const eventType = BID_WON; + const args = {wat: 'wot'}; events.emit(eventType, args); adapter.enableAnalytics(); @@ -65,9 +65,58 @@ FEATURE: Analytics Adapters API // As now AUCTION_DEBUG is triggered for WARNINGS too, the BID_RESPONSE goes last in the array const index = server.requests.length - 1; let result = JSON.parse(server.requests[index].requestBody); - expect(result).to.deep.equal({eventType: 'bidResponse', args: {wat: 'wot'}}); + expect(result).to.deep.equal({eventType, args: {wat: 'wot'}}); }); + describe('event filters', () => { + function fireEvents() { + events.emit(BID_WON, {}); + events.emit(NO_BID, {}); + } + function getEvents(ev) { + return server.requests + .map(r => JSON.parse(r.requestBody)) + .filter(r => r.eventType === ev) + } + + Object.entries({ + 'whitelist includeEvents': { + includeEvents: [BID_WON] + }, + 'blacklist excludeEvents': { + excludeEvents: [NO_BID] + }, + 'give precedence to exclude over include': { + includeEvents: [BID_WON, NO_BID], + excludeEvents: [NO_BID] + } + }).forEach(([t, config]) => { + it(`should ${t}`, () => { + fireEvents(); + adapter.enableAnalytics(config); + expect(getEvents(BID_WON).length).to.eql(1); + expect(getEvents(NO_BID).length).to.eql(0); + fireEvents(); + expect(getEvents(BID_WON).length).to.eql(2); + expect(getEvents(NO_BID).length).to.eql(0); + }) + }) + }) + + it('should prevent infinite loops when track triggers other events', () => { + let i = 0; + adapter.track = ((orig) => { + return function (event) { + i++; + orig.call(this, event); + events.emit(BID_WON, {}) + } + })(adapter.track); + adapter.enableAnalytics(config); + events.emit(BID_WON, {}); + expect(i >= 100).to.eql(false); + }) + describe(`WHEN an event occurs after enable analytics\n`, function () { beforeEach(function () { sinon.stub(events, 'getEvents').returns([]); // these tests shouldn't be affected by previous tests @@ -77,108 +126,26 @@ FEATURE: Analytics Adapters API events.getEvents.restore(); }); - it('SHOULD call global when a bidWon event occurs', function () { - const eventType = BID_WON; - const args = { more: 'info' }; - - adapter.enableAnalytics(); - events.emit(eventType, args); - - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {more: 'info'}, eventType: 'bidWon'}); - }); - - it('SHOULD call global when a adRenderFailed event occurs', function () { - const eventType = AD_RENDER_FAILED; - const args = { call: 'adRenderFailed' }; - - adapter.enableAnalytics(); - events.emit(eventType, args); - - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {call: 'adRenderFailed'}, eventType: 'adRenderFailed'}); - }); - - it('SHOULD call global when a adRenderSucceeded event occurs', function () { - const eventType = AD_RENDER_SUCCEEDED; - const args = { call: 'adRenderSucceeded' }; - - adapter.enableAnalytics(); - events.emit(eventType, args); - - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {call: 'adRenderSucceeded'}, eventType: 'adRenderSucceeded'}); - }); - - it('SHOULD call global when an auction debug event occurs', function () { - const eventType = AUCTION_DEBUG; - const args = { call: 'auctionDebug' }; - - adapter.enableAnalytics(); - events.emit(eventType, args); - - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {call: 'auctionDebug'}, eventType: 'auctionDebug'}); - }); - - it('SHOULD call global when an addAdUnits event occurs', function () { - const eventType = ADD_AD_UNITS; - const args = { call: 'addAdUnits' }; - - adapter.enableAnalytics(); - events.emit(eventType, args); - - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {call: 'addAdUnits'}, eventType: 'addAdUnits'}); - }); - - it('SHOULD call global when a requestBids event occurs', function () { - const eventType = REQUEST_BIDS; - const args = { call: 'request' }; - - adapter.enableAnalytics(); - events.emit(eventType, args); - - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {call: 'request'}, eventType: 'requestBids'}); - }); - - it('SHOULD call global when a bidRequest event occurs', function () { - const eventType = BID_REQUESTED; - const args = { call: 'request' }; - - adapter.enableAnalytics(); - events.emit(eventType, args); - - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {call: 'request'}, eventType: 'bidRequested'}); - }); - - it('SHOULD call global when a bidResponse event occurs', function () { - const eventType = BID_RESPONSE; - const args = { call: 'response' }; - - adapter.enableAnalytics(); - events.emit(eventType, args); - - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {call: 'response'}, eventType: 'bidResponse'}); - }); - - it('SHOULD call global when a bidTimeout event occurs', function () { - const eventType = BID_TIMEOUT; - const args = { call: 'timeout' }; + Object.values(DEFAULT_INCLUDE_EVENTS).forEach(eventType => { + it(`SHOULD call global when a ${eventType} event occurs`, () => { + const args = {more: 'info'}; - adapter.enableAnalytics(); - events.emit(eventType, args); + adapter.enableAnalytics(); + events.emit(eventType, args); - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {call: 'timeout'}, eventType: 'bidTimeout'}); + let result = JSON.parse(server.requests[server.requests.length - 1].requestBody); + sinon.assert.match(result, { + eventType, + args: { + more: 'info' + }, + }); + }); }); it('SHOULD NOT call global again when adapter.enableAnalytics is called with previous timeout', function () { - const eventType = BID_TIMEOUT; - const args = { call: 'timeout' }; + const eventType = BID_WON; + const args = {call: 'timeout'}; events.emit(eventType, args); adapter.enableAnalytics(); @@ -189,7 +156,7 @@ FEATURE: Analytics Adapters API describe(`AND sampling is enabled\n`, function () { const eventType = BID_WON; - const args = { more: 'info' }; + const args = {more: 'info'}; beforeEach(function () { sinon.stub(Math, 'random').returns(0.5); @@ -225,3 +192,39 @@ FEATURE: Analytics Adapters API }); }); }); + +describe('Analytics asynchronous event tracking', () => { + before(() => { + setDebounceDelay(100); + }); + after(() => { + setDebounceDelay(0); + }); + + let adapter, clock; + + beforeEach(() => { + clock = sinon.useFakeTimers(); + adapter = new AnalyticsAdapter(config); + adapter.track = sinon.stub(); + adapter.enableAnalytics({}); + }); + + afterEach(() => { + clock.restore(); + }) + + it('does not call track as long as events are coming', () => { + events.emit(BID_WON, {i: 0}); + sinon.assert.notCalled(adapter.track); + clock.tick(10); + events.emit(BID_WON, {i: 1}); + sinon.assert.notCalled(adapter.track); + clock.tick(10); + sinon.assert.notCalled(adapter.track); + clock.tick(100); + sinon.assert.calledTwice(adapter.track); + sinon.assert.calledWith(adapter.track.firstCall, {eventType: BID_WON, args: {i: 0}}); + sinon.assert.calledWith(adapter.track.secondCall, {eventType: BID_WON, args: {i: 1}}); + }); +}) diff --git a/test/spec/modules/adWMGAnalyticsAdapter_spec.js b/test/spec/modules/adWMGAnalyticsAdapter_spec.js index ab8336c7126..1e0da1bb3c8 100644 --- a/test/spec/modules/adWMGAnalyticsAdapter_spec.js +++ b/test/spec/modules/adWMGAnalyticsAdapter_spec.js @@ -1,6 +1,7 @@ import adWMGAnalyticsAdapter from 'modules/adWMGAnalyticsAdapter.js'; import { expect } from 'chai'; import { server } from 'test/mocks/xhr.js'; +import {expectEvents} from '../../helpers/analytics.js'; let adapterManager = require('src/adapterManager').default; let events = require('src/events'); let constants = require('src/constants.json'); @@ -140,14 +141,15 @@ describe('adWMG Analytics', function () { } }); - events.emit(constants.EVENTS.AUCTION_INIT, {timestamp, auctionId, timeout, adUnits}); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); - events.emit(constants.EVENTS.NO_BID, {}); - events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeoutArgs); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_WON, wonRequest); - sinon.assert.callCount(adWMGAnalyticsAdapter.track, 7); + expectEvents([ + [constants.EVENTS.AUCTION_INIT, {timestamp, auctionId, timeout, adUnits}], + [constants.EVENTS.BID_REQUESTED, {}], + [constants.EVENTS.BID_RESPONSE, bidResponse], + [constants.EVENTS.NO_BID, {}], + [constants.EVENTS.BID_TIMEOUT, bidTimeoutArgs], + [constants.EVENTS.AUCTION_END, {}], + [constants.EVENTS.BID_WON, wonRequest], + ]).to.beTrackedBy(adWMGAnalyticsAdapter.track); }); it('should be two xhr requests', function () { diff --git a/test/spec/modules/adagioAnalyticsAdapter_spec.js b/test/spec/modules/adagioAnalyticsAdapter_spec.js index aee85412104..581f3cb1b87 100644 --- a/test/spec/modules/adagioAnalyticsAdapter_spec.js +++ b/test/spec/modules/adagioAnalyticsAdapter_spec.js @@ -83,34 +83,27 @@ describe('adagio analytics adapter', () => { timeToRespond: 132, }; - // Step 1: Send bid requested event - events.emit(constants.EVENTS.BID_REQUESTED, bidRequest); + const testEvents = { + [constants.EVENTS.BID_REQUESTED]: bidRequest, + [constants.EVENTS.BID_RESPONSE]: bidResponse, + [constants.EVENTS.AUCTION_END]: {} + }; - // Step 2: Send bid response event - events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); + // Step 1-3: Send events + Object.entries(testEvents).forEach(([ev, payload]) => events.emit(ev, payload)); - // Step 3: Send auction end event - events.emit(constants.EVENTS.AUCTION_END, {}); + function eventItem(eventName, args) { + return sinon.match({ + action: 'pb-analytics-event', + ts: sinon.match((val) => val !== undefined), + data: { + eventName, + args + } + }) + } - sandbox.assert.callCount(adagioQueuePushSpy, 3); - - const call0 = adagioQueuePushSpy.getCall(0); - expect(call0.args[0].action).to.equal('pb-analytics-event'); - expect(call0.args[0].ts).to.not.be.undefined; - expect(call0.args[0].data).to.not.be.undefined; - expect(call0.args[0].data).to.deep.equal({eventName: constants.EVENTS.BID_REQUESTED, args: bidRequest}); - - const call1 = adagioQueuePushSpy.getCall(1); - expect(call1.args[0].action).to.equal('pb-analytics-event'); - expect(call1.args[0].ts).to.not.be.undefined; - expect(call1.args[0].data).to.not.be.undefined; - expect(call1.args[0].data).to.deep.equal({eventName: constants.EVENTS.BID_RESPONSE, args: bidResponse}); - - const call2 = adagioQueuePushSpy.getCall(2); - expect(call2.args[0].action).to.equal('pb-analytics-event'); - expect(call2.args[0].ts).to.not.be.undefined; - expect(call2.args[0].data).to.not.be.undefined; - expect(call2.args[0].data).to.deep.equal({eventName: constants.EVENTS.AUCTION_END, args: {}}); + Object.entries(testEvents).forEach(([ev, payload]) => sinon.assert.calledWith(adagioQueuePushSpy, eventItem(ev, payload))); }); }); diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js index 9e9d7dbe156..a72bb012fa1 100644 --- a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js @@ -297,7 +297,6 @@ describe('BidWatch Analytics', function () { expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).not.to.have.property('vendorData'); - sinon.assert.callCount(bidwatchAnalytics.track, 4); }); it('test bidWon', function() { @@ -318,7 +317,6 @@ describe('BidWatch Analytics', function () { expect(message).not.to.have.property('ad'); expect(message).to.have.property('adId') expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); - sinon.assert.callCount(bidwatchAnalytics.track, 1); }); }); }); diff --git a/test/spec/modules/concertAnalyticsAdapter_spec.js b/test/spec/modules/concertAnalyticsAdapter_spec.js index d130aea6043..1df73ae04fe 100644 --- a/test/spec/modules/concertAnalyticsAdapter_spec.js +++ b/test/spec/modules/concertAnalyticsAdapter_spec.js @@ -1,5 +1,6 @@ import concertAnalytics from 'modules/concertAnalyticsAdapter.js'; import { expect } from 'chai'; +import {expectEvents} from '../../helpers/analytics.js'; const sinon = require('sinon'); let adapterManager = require('src/adapterManager').default; let events = require('src/events'); @@ -46,10 +47,7 @@ describe('ConcertAnalyticsAdapter', function() { it('should catch all events', function() { sandbox.spy(concertAnalytics, 'track'); - - fireBidEvents(events); - // 5 Concert events + 1 Clean.io event - sandbox.assert.callCount(concertAnalytics.track, 6); + expectEvents().to.beTrackedBy(concertAnalytics.track); }); it('should report data for BID_RESPONSE, BID_WON events', function() { diff --git a/test/spec/modules/eplanningAnalyticsAdapter_spec.js b/test/spec/modules/eplanningAnalyticsAdapter_spec.js index bb9e6c4fb86..419181de983 100644 --- a/test/spec/modules/eplanningAnalyticsAdapter_spec.js +++ b/test/spec/modules/eplanningAnalyticsAdapter_spec.js @@ -152,9 +152,6 @@ describe('eplanning analytics adapter', function () { // Step 10 check that the host to send the ajax request is configurable via options expect(eplAnalyticsAdapter.context.host).to.equal(initOptions.host); - - // Step 11 verify that we received 7 events (6 E-Planning events + 1 Clean.io event) - sinon.assert.callCount(eplAnalyticsAdapter.track, 7); }); }); }); diff --git a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js index d97b0925713..3b3542d43b0 100644 --- a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js +++ b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js @@ -1,5 +1,6 @@ import invisiblyAdapter from 'modules/invisiblyAnalyticsAdapter.js'; import { expect } from 'chai'; +import {expectEvents} from '../../helpers/analytics.js'; let events = require('src/events'); let constants = require('src/constants.json'); @@ -197,14 +198,8 @@ describe('Invisibly Analytics Adapter test suite', function () { account: 'invisibly', }, }); - events.emit(constants.EVENTS.AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(constants.EVENTS.AUCTION_END, MOCK.AUCTION_END); - events.emit(constants.EVENTS.BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE); - events.emit(constants.EVENTS.BID_WON, MOCK.BID_WON); - // 5 Invisibly events + 1 Clean.io event - sinon.assert.callCount(invisiblyAdapter.track, 6); + expectEvents().to.beTrackedBy(invisiblyAdapter.track); }); it('should not catch events triggered without invisibly account config', function () { @@ -382,9 +377,6 @@ describe('Invisibly Analytics Adapter test suite', function () { expect(invisiblyEvents.event_data.pageViewId).to.exist; expect(invisiblyEvents.event_data.ver).to.equal(1); expect(invisiblyEvents.event_type).to.equal('PREBID_bidWon'); - - // 1 Invisibly event + 1 Clean.io event - sinon.assert.callCount(invisiblyAdapter.track, 2); }); // spec for bidder done event @@ -522,7 +514,6 @@ describe('Invisibly Analytics Adapter test suite', function () { expect(invisiblyEvents.event_data.auctionId).to.equal( MOCK.AUCTION_END.auctionId ); - sinon.assert.callCount(invisiblyAdapter.track, 1); }); // should not call sendEvent for events not supported by the adapter @@ -541,22 +532,21 @@ describe('Invisibly Analytics Adapter test suite', function () { it('track all event without errors', function () { invisiblyAdapter.enableAnalytics(MOCK.config); - events.emit(constants.EVENTS.AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(constants.EVENTS.AUCTION_END, MOCK.AUCTION_END); - events.emit(constants.EVENTS.BID_ADJUSTMENT, MOCK.BID_ADJUSTMENT); - events.emit(constants.EVENTS.BID_TIMEOUT, MOCK.BID_TIMEOUT); - events.emit(constants.EVENTS.BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE); - events.emit(constants.EVENTS.NO_BID, MOCK.NO_BID); - events.emit(constants.EVENTS.BID_WON, MOCK.BID_WON); - events.emit(constants.EVENTS.BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(constants.EVENTS.SET_TARGETING, MOCK.SET_TARGETING); - events.emit(constants.EVENTS.REQUEST_BIDS, MOCK.REQUEST_BIDS); - events.emit(constants.EVENTS.ADD_AD_UNITS, MOCK.ADD_AD_UNITS); - events.emit(constants.EVENTS.AD_RENDER_FAILED, MOCK.AD_RENDER_FAILED); - - // 13 Invisibly events + 1 Clean.io event - sinon.assert.callCount(invisiblyAdapter.track, 14); + expectEvents([ + constants.EVENTS.AUCTION_INIT, + constants.EVENTS.AUCTION_END, + constants.EVENTS.BID_ADJUSTMENT, + constants.EVENTS.BID_TIMEOUT, + constants.EVENTS.BID_REQUESTED, + constants.EVENTS.BID_RESPONSE, + constants.EVENTS.NO_BID, + constants.EVENTS.BID_WON, + constants.EVENTS.BIDDER_DONE, + constants.EVENTS.SET_TARGETING, + constants.EVENTS.REQUEST_BIDS, + constants.EVENTS.ADD_AD_UNITS, + constants.EVENTS.AD_RENDER_FAILED + ]).to.beTrackedBy(invisiblyAdapter.track); }); }); diff --git a/test/spec/modules/konduitAnalyticsAdapter_spec.js b/test/spec/modules/konduitAnalyticsAdapter_spec.js index 1612138cde4..e79ae2feeeb 100644 --- a/test/spec/modules/konduitAnalyticsAdapter_spec.js +++ b/test/spec/modules/konduitAnalyticsAdapter_spec.js @@ -121,7 +121,5 @@ describe(`Konduit Analytics Adapter`, () => { expect(requestBody.konduitId).to.be.equal(konduitId); expect(requestBody.prebidVersion).to.be.equal('$prebid.version$'); expect(requestBody.environment).to.be.an('object'); - // 6 Konduit events + 1 Clean.io event - sinon.assert.callCount(konduitAnalyticsAdapter.track, 7); }); }); diff --git a/test/spec/modules/liveIntentAnalyticsAdapter_spec.js b/test/spec/modules/liveIntentAnalyticsAdapter_spec.js index b3a452e5ece..fa4c5cd8cad 100644 --- a/test/spec/modules/liveIntentAnalyticsAdapter_spec.js +++ b/test/spec/modules/liveIntentAnalyticsAdapter_spec.js @@ -2,6 +2,7 @@ import liAnalytics from 'modules/liveIntentAnalyticsAdapter'; import { expect } from 'chai'; import { server } from 'test/mocks/xhr.js'; import { auctionManager } from 'src/auctionManager.js'; +import {expectEvents} from '../../helpers/analytics.js'; let utils = require('src/utils'); let refererDetection = require('src/refererDetection'); @@ -286,12 +287,8 @@ describe('LiveIntent Analytics Adapter ', () => { }); it('track is called', () => { - liAnalytics.enableAnalytics(config); sandbox.stub(liAnalytics, 'track'); - events.emit(constants.EVENTS.AUCTION_END, args); - events.emit(constants.EVENTS.AUCTION_END, args); - events.emit(constants.EVENTS.AUCTION_END, args); - clock.tick(6000); - sinon.assert.callCount(liAnalytics.track, 3) + liAnalytics.enableAnalytics(config); + expectEvents().to.beTrackedBy(liAnalytics.track); }) }); diff --git a/test/spec/modules/medianetAnalyticsAdapter_spec.js b/test/spec/modules/medianetAnalyticsAdapter_spec.js index b75437f2ccd..c408f23c4f4 100644 --- a/test/spec/modules/medianetAnalyticsAdapter_spec.js +++ b/test/spec/modules/medianetAnalyticsAdapter_spec.js @@ -3,6 +3,7 @@ import medianetAnalytics from 'modules/medianetAnalyticsAdapter.js'; import * as utils from 'src/utils.js'; import CONSTANTS from 'src/constants.json'; import * as events from 'src/events.js'; +import {clearEvents} from 'src/events.js'; const { EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, NO_BID, BID_TIMEOUT, AUCTION_END, SET_TARGETING, BID_WON } @@ -127,7 +128,12 @@ describe('Media.net Analytics Adapter', function() { options: { cid: CUSTOMER_ID } - }; + } + + before(() => { + clearEvents(); + }); + beforeEach(function () { sandbox = sinon.sandbox.create(); }); diff --git a/test/spec/modules/optimonAnalyticsAdapter_spec.js b/test/spec/modules/optimonAnalyticsAdapter_spec.js index 406e9cb75c4..f1aa00334b5 100644 --- a/test/spec/modules/optimonAnalyticsAdapter_spec.js +++ b/test/spec/modules/optimonAnalyticsAdapter_spec.js @@ -4,6 +4,7 @@ import optimonAnalyticsAdapter from '../../../modules/optimonAnalyticsAdapter.js import adapterManager from 'src/adapterManager'; import * as events from 'src/events'; import constants from 'src/constants.json' +import {expectEvents} from '../../helpers/analytics.js'; const AD_UNIT_CODE = 'demo-adunit-1'; const PUBLISHER_CONFIG = { @@ -14,14 +15,12 @@ const PUBLISHER_CONFIG = { describe('Optimon Analytics Adapter', () => { const optmn_currentWindow = utils.getWindowSelf(); - let optmn_queue = []; beforeEach(() => { - optmn_currentWindow.OptimonAnalyticsAdapter = (...optmn_args) => optmn_queue.push(optmn_args); + optmn_currentWindow.OptimonAnalyticsAdapter = sinon.stub() adapterManager.enableAnalytics({ provider: 'optimon' }); - optmn_queue = [] }); afterEach(() => { @@ -29,13 +28,6 @@ describe('Optimon Analytics Adapter', () => { }); it('should forward all events to the queue', () => { - const optmn_arguments = [AD_UNIT_CODE, PUBLISHER_CONFIG]; - - events.emit(constants.EVENTS.AUCTION_END, optmn_arguments) - events.emit(constants.EVENTS.BID_TIMEOUT, optmn_arguments) - events.emit(constants.EVENTS.BID_WON, optmn_arguments) - - // 3 Optimon events + 1 Clean.io event - expect(optmn_queue.length).to.eql(4); + expectEvents().to.beBundledTo(optmn_currentWindow.OptimonAnalyticsAdapter); }); }); diff --git a/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js b/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js index 49b048f1fe6..0c4949264a7 100644 --- a/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js +++ b/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js @@ -52,19 +52,12 @@ describe('Piano DMP Analytics Adapter', () => { // Then const callQueue = (window.cX || {}).callQueue; - const billableEventIndex = callQueue.findIndex(([, params]) => params.eventType === constants.EVENTS.BILLABLE_EVENT); - if (billableEventIndex > -1) { - callQueue.splice(billableEventIndex, 1); - } - expect(callQueue).to.be.an('array'); - expect(callQueue.length).to.equal(testEvents.length); - - callQueue.forEach(([method, params], index) => { + testEvents.forEach(({event, args}) => { + const [method, params] = callQueue.filter(item => item[1].eventType === event)[0]; expect(method).to.equal('prebid'); - expect(params.eventType).to.equal(testEvents[index].event); - expect(params.params).to.deep.equal(testEvents[index].args); - }); + expect(params.params).to.deep.equal(args); + }) }); }); }); diff --git a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js index 2e1bf4df062..522a78627d7 100644 --- a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js +++ b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js @@ -1,9 +1,8 @@ -import prebidmanagerAnalytics, { - storage -} from 'modules/prebidmanagerAnalyticsAdapter.js'; +import prebidmanagerAnalytics, {storage} from 'modules/prebidmanagerAnalyticsAdapter.js'; import {expect} from 'chai'; import {server} from 'test/mocks/xhr.js'; import * as utils from 'src/utils.js'; +import {expectEvents} from '../../helpers/analytics.js'; let events = require('src/events'); let constants = require('src/constants.json'); @@ -95,15 +94,7 @@ describe('Prebid Manager Analytics Adapter', function () { } }); - events.emit(constants.EVENTS.AUCTION_INIT, {}); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, {}); - events.emit(constants.EVENTS.BID_WON, {}); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_TIMEOUT, {}); - - // 6 Prebid Manager events + 1 Clean.io event - sinon.assert.callCount(prebidmanagerAnalytics.track, 7); + expectEvents().to.beTrackedBy(prebidmanagerAnalytics.track); }); }); diff --git a/test/spec/modules/pubperfAnalyticsAdapter_spec.js b/test/spec/modules/pubperfAnalyticsAdapter_spec.js index 160529125d3..9949d87a2bc 100644 --- a/test/spec/modules/pubperfAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubperfAnalyticsAdapter_spec.js @@ -1,9 +1,10 @@ import pubperfAnalytics from 'modules/pubperfAnalyticsAdapter.js'; -import { expect } from 'chai'; -import { server } from 'test/mocks/xhr.js'; +import {expect} from 'chai'; +import {server} from 'test/mocks/xhr.js'; +import {expectEvents, fireEvents} from '../../helpers/analytics.js'; + let events = require('src/events'); let utils = require('src/utils.js'); -let constants = require('src/constants.json'); describe('Pubperf Analytics Adapter', function() { describe('Prebid Manager Analytic tests', function() { @@ -22,13 +23,7 @@ describe('Pubperf Analytics Adapter', function() { provider: 'pubperf' }); - events.emit(constants.EVENTS.AUCTION_INIT, {}); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, {}); - events.emit(constants.EVENTS.BID_WON, {}); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_TIMEOUT, {}); - + fireEvents(); expect(server.requests.length).to.equal(0); expect(utils.logError.called).to.equal(true); }); @@ -42,15 +37,7 @@ describe('Pubperf Analytics Adapter', function() { provider: 'pubperf' }); - events.emit(constants.EVENTS.AUCTION_INIT, {}); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, {}); - events.emit(constants.EVENTS.BID_WON, {}); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_TIMEOUT, {}); - - // 6 Pubperf events + 1 Clean.io event - sinon.assert.callCount(pubperfAnalytics.track, 7); + expectEvents().to.beTrackedBy(pubperfAnalytics.track); }); }); }); diff --git a/test/spec/modules/pubstackAnalyticsAdapter_spec.js b/test/spec/modules/pubstackAnalyticsAdapter_spec.js index 3d01a0bb506..fe7441e91e5 100644 --- a/test/spec/modules/pubstackAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubstackAnalyticsAdapter_spec.js @@ -3,17 +3,16 @@ import pubstackAnalytics from '../../../modules/pubstackAnalyticsAdapter.js'; import adapterManager from 'src/adapterManager'; import * as events from 'src/events'; import constants from 'src/constants.json' +import {expectEvents} from '../../helpers/analytics.js'; describe('Pubstack Analytics Adapter', () => { const scope = utils.getWindowSelf(); - let queue = []; beforeEach(() => { - scope.PubstackAnalytics = (...args) => queue.push(args); + scope.PubstackAnalytics = sinon.stub(); adapterManager.enableAnalytics({ provider: 'pubstack' }); - queue = [] }); afterEach(() => { @@ -21,19 +20,6 @@ describe('Pubstack Analytics Adapter', () => { }); it('should forward all events to the queue', () => { - // Given - const args = 'any-args' - - // When - events.emit(constants.EVENTS.AUCTION_END, args) - events.emit(constants.EVENTS.BID_REQUESTED, args) - events.emit(constants.EVENTS.BID_ADJUSTMENT, args) - events.emit(constants.EVENTS.BID_RESPONSE, args) - events.emit(constants.EVENTS.BID_WON, args) - events.emit(constants.EVENTS.NO_BID, args) - - // Then - // 6 Pubstack events + 1 Clean.io event - expect(queue.length).to.eql(7); + expectEvents().to.beBundledTo(scope.PubstackAnalytics); }); }); diff --git a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js index dd5c223b767..e14582edc39 100644 --- a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js @@ -1,6 +1,7 @@ -import { expect } from 'chai'; +import {expect} from 'chai'; import pubwiseAnalytics from 'modules/pubwiseAnalyticsAdapter.js'; -import {server} from 'test/mocks/xhr.js'; +import {expectEvents} from '../../helpers/analytics.js'; + let events = require('src/events'); let adapterManager = require('src/adapterManager').default; let constants = require('src/constants.json'); @@ -58,23 +59,16 @@ describe('PubWise Prebid Analytics', function () { sandbox.spy(pubwiseAnalytics, 'track'); - // sent - events.emit(constants.EVENTS.AUCTION_INIT, mock.AUCTION_INIT); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, {}); - events.emit(constants.EVENTS.BID_WON, {}); - events.emit(constants.EVENTS.AD_RENDER_FAILED, {}); - events.emit(constants.EVENTS.TCF2_ENFORCEMENT, {}); - events.emit(constants.EVENTS.BID_TIMEOUT, {}); - - // forces flush - events.emit(constants.EVENTS.AUCTION_END, {}); - - // eslint-disable-next-line - //console.log(requests); - - /* testing for 6 calls, including the 2 we're not currently tracking */ - sandbox.assert.callCount(pubwiseAnalytics.track, 8); + expectEvents([ + constants.EVENTS.AUCTION_INIT, + constants.EVENTS.BID_REQUESTED, + constants.EVENTS.BID_RESPONSE, + constants.EVENTS.BID_WON, + constants.EVENTS.AD_RENDER_FAILED, + constants.EVENTS.TCF2_ENFORCEMENT, + constants.EVENTS.BID_TIMEOUT, + constants.EVENTS.AUCTION_END, + ]).to.beTrackedBy(pubwiseAnalytics.track); }); it('should initialize the auction properly', function () { diff --git a/test/spec/modules/sigmoidAnalyticsAdapter_spec.js b/test/spec/modules/sigmoidAnalyticsAdapter_spec.js index 6d772de02eb..6cdc3c448b9 100644 --- a/test/spec/modules/sigmoidAnalyticsAdapter_spec.js +++ b/test/spec/modules/sigmoidAnalyticsAdapter_spec.js @@ -1,5 +1,7 @@ import sigmoidAnalytic from 'modules/sigmoidAnalyticsAdapter.js'; -import { expect } from 'chai'; +import {expect} from 'chai'; +import {expectEvents} from '../../helpers/analytics.js'; + let events = require('src/events'); let adapterManager = require('src/adapterManager').default; let constants = require('src/constants.json'); @@ -32,13 +34,7 @@ describe('sigmoid Prebid Analytic', function () { } }); - events.emit(constants.EVENTS.AUCTION_INIT, {}); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, {}); - events.emit(constants.EVENTS.BID_WON, {}); - - sinon.assert.callCount(sigmoidAnalytic.track, 8); + expectEvents().to.beTrackedBy(sigmoidAnalytic.track); }); }); describe('build utm tag data', function () { diff --git a/test/spec/modules/sovrnAnalyticsAdapter_spec.js b/test/spec/modules/sovrnAnalyticsAdapter_spec.js index 8284bb54e9b..68552eb3d8a 100644 --- a/test/spec/modules/sovrnAnalyticsAdapter_spec.js +++ b/test/spec/modules/sovrnAnalyticsAdapter_spec.js @@ -1,8 +1,10 @@ import sovrnAnalyticsAdapter from '../../../modules/sovrnAnalyticsAdapter.js'; -import { expect } from 'chai'; +import {expect} from 'chai'; import {config} from 'src/config.js'; import adaptermanager from 'src/adapterManager.js'; -import { server } from 'test/mocks/xhr.js'; +import {server} from 'test/mocks/xhr.js'; +import {expectEvents, fireEvents} from '../../helpers/analytics.js'; + var assert = require('assert'); let events = require('src/events'); @@ -195,15 +197,7 @@ describe('Sovrn Analytics Adapter', function () { sovrnId: 123 } }); - - events.emit(constants.EVENTS.AUCTION_INIT, {}); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, {}); - events.emit(constants.EVENTS.BID_WON, {}); - - // 5 SovrnAnalytics events + 1 Clean.io event - sinon.assert.callCount(sovrnAnalyticsAdapter.track, 6); + expectEvents().to.beTrackedBy(sovrnAnalyticsAdapter.track); }); it('should catch no events if no affiliate id', function () { @@ -212,13 +206,7 @@ describe('Sovrn Analytics Adapter', function () { options: { } }); - - events.emit(constants.EVENTS.AUCTION_INIT, {}); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, {}); - events.emit(constants.EVENTS.BID_WON, {}); - + fireEvents(); sinon.assert.callCount(sovrnAnalyticsAdapter.track, 0); }); }); diff --git a/test/spec/modules/yieldoneAnalyticsAdapter_spec.js b/test/spec/modules/yieldoneAnalyticsAdapter_spec.js index f55577913a5..ea52f89773e 100644 --- a/test/spec/modules/yieldoneAnalyticsAdapter_spec.js +++ b/test/spec/modules/yieldoneAnalyticsAdapter_spec.js @@ -1,6 +1,7 @@ import yieldoneAnalytics from 'modules/yieldoneAnalyticsAdapter.js'; import { targeting } from 'src/targeting.js'; import { expect } from 'chai'; +import _ from 'lodash'; let events = require('src/events'); let adapterManager = require('src/adapterManager').default; let constants = require('src/constants.json'); @@ -225,7 +226,9 @@ describe('Yieldone Prebid Analytic', function () { pubId: initOptions.pubId, page: {url: testReferrer}, wrapper_version: '$prebid.version$', - events: expectedEvents + events: sinon.match(evs => { + return !expectedEvents.some((expectedEvent) => evs.find(ev => _.isEqual(ev, expectedEvent)) === -1) + }) }; const preparedWinnerParams = Object.assign({adServerTargeting: fakeTargeting}, winner); @@ -262,7 +265,7 @@ describe('Yieldone Prebid Analytic', function () { events.emit(constants.EVENTS.AUCTION_END, auctionEnd); - expect(yieldoneAnalytics.eventsStorage[auctionId]).to.deep.equal(expectedResult); + sinon.assert.match(yieldoneAnalytics.eventsStorage[auctionId], expectedResult); delete yieldoneAnalytics.eventsStorage[auctionId]; From 54d225674f26446fddee1a85103e179fd98e4bc4 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Mon, 28 Nov 2022 11:53:43 -0500 Subject: [PATCH 474/569] JW Player RTD Module: prefer segment.id to segment.value (#9153) * removes id * updates docs * Revert "updates docs" This reverts commit 926a06dd9581868e6fd9e682e68b48592aebbd3e. * Revert "removes id" This reverts commit c3a1be70e7274451b6b60381c036385b579eb23b. * drop segment.value --- modules/gridBidAdapter.js | 2 +- modules/gridNMBidAdapter.js | 2 +- modules/jwplayerRtdProvider.js | 3 +-- test/spec/modules/jwplayerRtdProvider_spec.js | 6 +++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 3aaeaf73580..b300c5c58c0 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -652,7 +652,7 @@ function getUserIdFromFPDStorage() { function segmentProcessing(segment, forceSegName) { return segment .map((seg) => { - const value = seg && (seg.value || seg.id || seg); + const value = seg && (seg.id || seg); if (typeof value === 'string' || typeof value === 'number') { return { value: value.toString(), diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 6f8eaba43d3..c49b7619c07 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -432,7 +432,7 @@ export function getSyncUrl() { function segmentProcessing(segment, forceSegName) { return segment .map((seg) => { - const value = seg && (seg.value || seg.id || seg); + const value = seg && (seg.id || seg); if (typeof value === 'string' || typeof value === 'number') { return { value: value.toString(), diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index 342531ba26e..b79843dccfd 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -274,8 +274,7 @@ export function getContentSegments(segments) { const formattedSegments = segments.reduce((convertedSegments, rawSegment) => { convertedSegments.push({ - id: rawSegment, - value: rawSegment + id: rawSegment }); return convertedSegments; }, []); diff --git a/test/spec/modules/jwplayerRtdProvider_spec.js b/test/spec/modules/jwplayerRtdProvider_spec.js index 77c8ce58442..5a38a971e09 100644 --- a/test/spec/modules/jwplayerRtdProvider_spec.js +++ b/test/spec/modules/jwplayerRtdProvider_spec.js @@ -659,9 +659,9 @@ describe('jwplayerRtdProvider', function() { const segment2 = 'segment2'; const segment3 = 'segment3'; const contentSegments = getContentSegments([segment1, segment2, segment3]); - expect(contentSegments[0]).to.deep.equal({ id: segment1, value: segment1 }); - expect(contentSegments[1]).to.deep.equal({ id: segment2, value: segment2 }); - expect(contentSegments[2]).to.deep.equal({ id: segment3, value: segment3 }); + expect(contentSegments[0]).to.deep.equal({ id: segment1 }); + expect(contentSegments[1]).to.deep.equal({ id: segment2 }); + expect(contentSegments[2]).to.deep.equal({ id: segment3 }); }); }); From 4f21a5bc9e2e4ace066a4d26ca1c9c637c46a477 Mon Sep 17 00:00:00 2001 From: AcuityAdsIntegrations <72594990+AcuityAdsIntegrations@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:56:39 +0200 Subject: [PATCH 475/569] AcuityAds adapter: fix issue with download (#9164) * add prebid.js adapter * changes * changes * changes * changes * fix downolad --- modules/{acuityAdsBidAdapter.js => acuityadsBidAdapter.js} | 0 modules/{acuityAdsBidAdapter.md => acuityadsBidAdapter.md} | 0 ...{acuityAdsBidAdapter_spec.js => acuityadsBidAdapter_spec.js} | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename modules/{acuityAdsBidAdapter.js => acuityadsBidAdapter.js} (100%) rename modules/{acuityAdsBidAdapter.md => acuityadsBidAdapter.md} (100%) rename test/spec/modules/{acuityAdsBidAdapter_spec.js => acuityadsBidAdapter_spec.js} (99%) diff --git a/modules/acuityAdsBidAdapter.js b/modules/acuityadsBidAdapter.js similarity index 100% rename from modules/acuityAdsBidAdapter.js rename to modules/acuityadsBidAdapter.js diff --git a/modules/acuityAdsBidAdapter.md b/modules/acuityadsBidAdapter.md similarity index 100% rename from modules/acuityAdsBidAdapter.md rename to modules/acuityadsBidAdapter.md diff --git a/test/spec/modules/acuityAdsBidAdapter_spec.js b/test/spec/modules/acuityadsBidAdapter_spec.js similarity index 99% rename from test/spec/modules/acuityAdsBidAdapter_spec.js rename to test/spec/modules/acuityadsBidAdapter_spec.js index 18ea574c1ce..2497b435a19 100644 --- a/test/spec/modules/acuityAdsBidAdapter_spec.js +++ b/test/spec/modules/acuityadsBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from '../../../modules/acuityAdsBidAdapter'; +import { spec } from '../../../modules/acuityadsBidAdapter'; import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; import { getUniqueIdentifierStr } from '../../../src/utils.js'; From ebf6272f175160688fbccb5213a0fad98324723c Mon Sep 17 00:00:00 2001 From: Giovanni Sollazzo Date: Mon, 28 Nov 2022 18:00:59 +0100 Subject: [PATCH 476/569] AIDEM Bid Adapter: initial adapter release (#9222) * AIDEM Bid Adapter * Added _spec.js * update * Fix Navigator in _spec.js * Removed timeout handler. * Added publisherId as required bidder params * moved publisherId into site publisher object Co-authored-by: darkstar --- modules/aidemBidAdapter.js | 492 ++++++++++++++++ modules/aidemBidAdapter.md | 187 ++++++ test/spec/modules/aidemBidAdapter_spec.js | 664 ++++++++++++++++++++++ 3 files changed, 1343 insertions(+) create mode 100644 modules/aidemBidAdapter.js create mode 100644 modules/aidemBidAdapter.md create mode 100644 test/spec/modules/aidemBidAdapter_spec.js diff --git a/modules/aidemBidAdapter.js b/modules/aidemBidAdapter.js new file mode 100644 index 00000000000..41bf680db1c --- /dev/null +++ b/modules/aidemBidAdapter.js @@ -0,0 +1,492 @@ +import {_each, contains, deepAccess, deepSetValue, getDNT, isBoolean, isStr, logError, logInfo} from '../src/utils.js'; +import {config} from '../src/config.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {ajax} from '../src/ajax.js'; + +const BIDDER_CODE = 'aidem'; +const BASE_URL = 'https://zero.aidemsrv.com'; +const LOCAL_BASE_URL = 'http://127.0.0.1:8787'; + +const AVAILABLE_CURRENCIES = ['USD']; +const DEFAULT_CURRENCY = ['USD']; // NOTE - USD is the only supported currency right now; Hardcoded for bids +const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; +const REQUIRED_VIDEO_PARAMS = [ 'mimes', 'protocols', 'context' ]; + +export const ERROR_CODES = { + BID_SIZE_INVALID_FORMAT: 1, + BID_SIZE_NOT_INCLUDED: 2, + PROPERTY_NOT_INCLUDED: 3, + SITE_ID_INVALID_VALUE: 4, + MEDIA_TYPE_NOT_SUPPORTED: 5, + PUBLISHER_ID_INVALID_VALUE: 6, +}; + +const endpoints = { + request: `${BASE_URL}/bid/request`, + notice: { + win: `${BASE_URL}/notice/win`, + timeout: `${BASE_URL}/notice/timeout`, + error: `${BASE_URL}/notice/error`, + } +} + +export function setEndPoints(env = null, path = '', mediaType = BANNER) { + switch (env) { + case 'local': + endpoints.request = mediaType === BANNER ? `${LOCAL_BASE_URL}${path}/bid/request` : `${LOCAL_BASE_URL}${path}/bid/videorequest` + endpoints.notice.win = `${LOCAL_BASE_URL}${path}/notice/win` + endpoints.notice.error = `${LOCAL_BASE_URL}${path}/notice/error` + endpoints.notice.timeout = `${LOCAL_BASE_URL}${path}/notice/timeout` + break; + case 'main': + endpoints.request = mediaType === BANNER ? `${BASE_URL}${path}/bid/request` : `${BASE_URL}${path}/bid/videorequest` + endpoints.notice.win = `${BASE_URL}${path}/notice/win` + endpoints.notice.error = `${BASE_URL}${path}/notice/error` + endpoints.notice.timeout = `${BASE_URL}${path}/notice/timeout` + break; + } + return endpoints +} + +config.getConfig('aidem', function (config) { + if (config.aidem.env) { setEndPoints(config.aidem.env, config.aidem.path, config.aidem.mediaType) } +}) + +// AIDEM Custom FN +function recur(obj) { + var result = {}; var _tmp; + for (var i in obj) { + // enabledPlugin is too nested, also skip functions + if (!(i === 'enabledPlugin' || typeof obj[i] === 'function')) { + if (typeof obj[i] === 'object' && obj[i] !== null) { + // get props recursively + _tmp = recur(obj[i]); + // if object is not {} + if (Object.keys(_tmp).length) { + result[i] = _tmp; + } + } else { + // string, number or boolean + result[i] = obj[i]; + } + } + } + return result; +} + +// ================================================================================= +function getConnectionType() { + const connection = navigator.connection || navigator.webkitConnection; + if (!connection) { + return 0; + } + switch (connection.type) { + case 'ethernet': + return 1; + case 'wifi': + return 2; + case 'cellular': + switch (connection.effectiveType) { + case 'slow-2g': + return 4; + case '2g': + return 4; + case '3g': + return 5; + case '4g': + return 6; + case '5g': + return 7; + default: + return 3; + } + default: + return 0; + } +} + +function getDevice() { + const language = navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage; + return { + ua: navigator.userAgent, + dnt: !!getDNT(), + language: language, + connectiontype: getConnectionType(), + screen_width: screen.width, + screen_height: screen.height + }; +} + +function getRegs() { + let regs = {}; + const consentManagement = config.getConfig('consentManagement') + const coppa = config.getConfig('coppa') + if (consentManagement && !!(consentManagement.gdpr)) { + deepSetValue(regs, 'gdpr_applies', !!consentManagement.gdpr); + } else { + deepSetValue(regs, 'gdpr_applies', false); + } + if (consentManagement && deepAccess(consentManagement, 'usp.cmpApi') === 'static') { + deepSetValue(regs, 'usp_applies', !!deepAccess(consentManagement, 'usp')); + deepSetValue(regs, 'us_privacy', deepAccess(consentManagement, 'usp.consentData.getUSPData.uspString')); + } else { + deepSetValue(regs, 'usp_applies', false); + } + + if (isBoolean(coppa)) { + deepSetValue(regs, 'coppa_applies', !!coppa); + } else { + deepSetValue(regs, 'coppa_applies', false); + } + + return regs; +} + +function getPageUrl(bidderRequest) { + return bidderRequest?.refererInfo?.page +} + +function buildWinNotice(bid) { + return { + burl: deepAccess(bid, 'meta.burl'), + cpm: bid.cpm, + currency: bid.currency, + impid: deepAccess(bid, 'meta.impid'), + dsp_id: deepAccess(bid, 'meta.dsp_id'), + adUnitCode: bid.adUnitCode, + auctionId: bid.auctionId, + transactionId: bid.transactionId, + ttl: bid.ttl, + requestTimestamp: bid.requestTimestamp, + responseTimestamp: bid.responseTimestamp, + } +} + +function buildErrorNotice(prebidErrorResponse) { + return { + message: `Prebid.js: Server call for ${prebidErrorResponse.bidderCode} failed.`, + url: encodeURIComponent(getPageUrl(prebidErrorResponse)), + auctionId: prebidErrorResponse.auctionId, + bidderRequestId: prebidErrorResponse.bidderRequestId, + metrics: {} + } +} + +function hasValidFloor(obj) { + if (!obj) return false + const hasValue = !isNaN(Number(obj.value)) + const hasCurrency = contains(AVAILABLE_CURRENCIES, obj.currency) + return hasValue && hasCurrency +} + +function getMediaType(bidRequest) { + if ((bidRequest.mediaTypes && bidRequest.mediaTypes.hasOwnProperty('video')) || bidRequest.params.hasOwnProperty('video')) { return VIDEO } + return BANNER +} + +function getPrebidRequestFields(bidderRequest, bidRequests) { + const payload = {} + // Base Payload Data + deepSetValue(payload, 'id', bidderRequest.auctionId); + // Impressions + setPrebidImpressionObject(bidRequests, payload) + // Device + deepSetValue(payload, 'device', getDevice()) + // Timeout + deepSetValue(payload, 'tmax', bidderRequest.timeout); + // Currency + deepSetValue(payload, 'cur', DEFAULT_CURRENCY); + // Timezone + deepSetValue(payload, 'tz', new Date().getTimezoneOffset()); + // Privacy Regs + deepSetValue(payload, 'regs', getRegs()); + // Site + setPrebidSiteObject(bidderRequest, payload) + // Environment + setPrebidRequestEnvironment(payload) + // AT auction type + deepSetValue(payload, 'at', 1); + + return payload +} + +function setPrebidImpressionObject(bidRequests, payload) { + payload.imp = []; + _each(bidRequests, function (bidRequest) { + const impressionObject = {}; + // Placement or ad tag used to initiate the auction + deepSetValue(impressionObject, 'id', bidRequest.bidId); + // Transaction id + deepSetValue(impressionObject, 'tid', deepAccess(bidRequest, 'transactionId')); + // Publisher id + deepSetValue(payload, 'site.publisher.id', deepAccess(bidRequest, 'params.publisherId')); + // Site id + deepSetValue(payload, 'site.id', deepAccess(bidRequest, 'params.siteId')); + const mediaType = getMediaType(bidRequest) + switch (mediaType) { + case 'banner': + setPrebidImpressionObjectBanner(bidRequest, impressionObject) + break; + case 'video': + setPrebidImpressionObjectVideo(bidRequest, impressionObject) + break; + } + + // Floor (optional) + setPrebidImpressionObjectFloor(bidRequest, impressionObject) + + impressionObject.imp_ext = {}; + + payload.imp.push(impressionObject); + }); +} + +function setPrebidSiteObject(bidderRequest, payload) { + deepSetValue(payload, 'site.domain', deepAccess(bidderRequest, 'refererInfo.domain')); + deepSetValue(payload, 'site.page', deepAccess(bidderRequest, 'refererInfo.page')); + deepSetValue(payload, 'site.referer', deepAccess(bidderRequest, 'refererInfo.ref')); + deepSetValue(payload, 'site.cat', deepAccess(bidderRequest, 'ortb2.site.cat')); + deepSetValue(payload, 'site.sectioncat', deepAccess(bidderRequest, 'ortb2.site.sectioncat')); + deepSetValue(payload, 'site.keywords', deepAccess(bidderRequest, 'ortb2.site.keywords')); + deepSetValue(payload, 'site.site_ext', deepAccess(bidderRequest, 'ortb2.site.ext')); // see https://docs.prebid.org/features/firstPartyData.html +} + +function setPrebidRequestEnvironment(payload) { + const __navigator = JSON.parse(JSON.stringify(recur(navigator))); + delete __navigator.plugins; + deepSetValue(payload, 'environment.ri', getRefererInfo()); + deepSetValue(payload, 'environment.hl', window.history.length); + deepSetValue(payload, 'environment.nav', __navigator); + deepSetValue(payload, 'environment.inp.euc', window.encodeURIComponent.name === 'encodeURIComponent' && typeof window.encodeURIComponent.prototype === 'undefined'); + deepSetValue(payload, 'environment.inp.eu', window.encodeURI.name === 'encodeURI' && typeof window.encodeURI.prototype === 'undefined'); + deepSetValue(payload, 'environment.inp.js', window.JSON.stringify.name === 'stringify' && typeof window.JSON.stringify.prototype === 'undefined'); + deepSetValue(payload, 'environment.inp.jp', window.JSON.parse.name === 'parse' && typeof window.JSON.parse.prototype === 'undefined'); + deepSetValue(payload, 'environment.inp.ofe', window.Object.fromEntries.name === 'fromEntries' && typeof window.Object.fromEntries.prototype === 'undefined'); + deepSetValue(payload, 'environment.inp.oa', window.Object.assign.name === 'assign' && typeof window.Object.assign.prototype === 'undefined'); +} + +function setPrebidImpressionObjectFloor(bidRequest, impressionObject) { + const floor = deepAccess(bidRequest, 'params.floor') + if (hasValidFloor(floor)) { + deepSetValue(impressionObject, 'floor.value', floor.value) + deepSetValue(impressionObject, 'floor.currency', floor.currency) + } +} + +function setPrebidImpressionObjectBanner(bidRequest, impressionObject) { + deepSetValue(impressionObject, 'mediatype', BANNER); + deepSetValue(impressionObject, 'banner.topframe', 1); + deepSetValue(impressionObject, 'banner.format', []); + _each(bidRequest.mediaTypes.banner.sizes, function (bannerFormat) { + const format = {}; + deepSetValue(format, 'w', bannerFormat[0]); + deepSetValue(format, 'h', bannerFormat[1]); + deepSetValue(format, 'format_ext', {}); + impressionObject.banner.format.push(format); + }); +} + +function setPrebidImpressionObjectVideo(bidRequest, impressionObject) { + deepSetValue(impressionObject, 'mediatype', VIDEO); + deepSetValue(impressionObject, 'video.format', []); + deepSetValue(impressionObject, 'video.mimes', bidRequest.mediaTypes.video.mimes); + deepSetValue(impressionObject, 'video.minDuration', bidRequest.mediaTypes.video.minduration); + deepSetValue(impressionObject, 'video.maxDuration', bidRequest.mediaTypes.video.maxduration); + deepSetValue(impressionObject, 'video.protocols', bidRequest.mediaTypes.video.protocols); + deepSetValue(impressionObject, 'video.context', bidRequest.mediaTypes.video.context); + deepSetValue(impressionObject, 'video.playbackmethod', bidRequest.mediaTypes.video.playbackmethod); + deepSetValue(impressionObject, 'skip', bidRequest.mediaTypes.video.skip); + deepSetValue(impressionObject, 'skipafter', bidRequest.mediaTypes.video.skipafter); + deepSetValue(impressionObject, 'video.pos', bidRequest.mediaTypes.video.pos); + _each(bidRequest.mediaTypes.video.playerSize, function (videoPlayerSize) { + const format = {}; + deepSetValue(format, 'w', videoPlayerSize[0]); + deepSetValue(format, 'h', videoPlayerSize[1]); + deepSetValue(format, 'format_ext', {}); + impressionObject.video.format.push(format); + }); +} + +function getPrebidResponseBidObject(openRTBResponseBidObject) { + const prebidResponseBidObject = {}; + // Common properties + deepSetValue(prebidResponseBidObject, 'requestId', openRTBResponseBidObject.impid); + deepSetValue(prebidResponseBidObject, 'cpm', parseFloat(openRTBResponseBidObject.price)); + deepSetValue(prebidResponseBidObject, 'creativeId', openRTBResponseBidObject.crid); + deepSetValue(prebidResponseBidObject, 'currency', openRTBResponseBidObject.cur ? openRTBResponseBidObject.cur.toUpperCase() : DEFAULT_CURRENCY); + deepSetValue(prebidResponseBidObject, 'width', openRTBResponseBidObject.w); + deepSetValue(prebidResponseBidObject, 'height', openRTBResponseBidObject.h); + deepSetValue(prebidResponseBidObject, 'dealId', openRTBResponseBidObject.dealid) + deepSetValue(prebidResponseBidObject, 'netRevenue', true); + deepSetValue(prebidResponseBidObject, 'ttl', 60000); + + if (openRTBResponseBidObject.mediatype === VIDEO) { + logInfo('bidObject.mediatype == VIDEO'); + deepSetValue(prebidResponseBidObject, 'mediaType', VIDEO); + deepSetValue(prebidResponseBidObject, 'vastUrl', openRTBResponseBidObject.adm); + } else { + logInfo('bidObject.mediatype == BANNER'); + deepSetValue(prebidResponseBidObject, 'mediaType', BANNER); + deepSetValue(prebidResponseBidObject, 'ad', openRTBResponseBidObject.adm); + } + setPrebidResponseBidObjectMeta(prebidResponseBidObject, openRTBResponseBidObject) + return prebidResponseBidObject +} + +function setPrebidResponseBidObjectMeta(prebidResponseBidObject, openRTBResponseBidObject) { + logInfo('AIDEM Bid Adapter meta', openRTBResponseBidObject); + deepSetValue(prebidResponseBidObject, 'meta.advertiserDomains', openRTBResponseBidObject.adomain); + if (openRTBResponseBidObject.cat && Array.isArray(openRTBResponseBidObject.cat)) { + const primaryCatId = openRTBResponseBidObject.cat.shift(); + deepSetValue(prebidResponseBidObject, 'meta.primaryCatId', primaryCatId); + deepSetValue(prebidResponseBidObject, 'meta.secondaryCatIds', openRTBResponseBidObject.cat); + } + deepSetValue(prebidResponseBidObject, 'meta.id', openRTBResponseBidObject.id); + deepSetValue(prebidResponseBidObject, 'meta.dsp_id', openRTBResponseBidObject.dsp_id); + deepSetValue(prebidResponseBidObject, 'meta.adid', openRTBResponseBidObject.adid); + deepSetValue(prebidResponseBidObject, 'meta.burl', openRTBResponseBidObject.burl); + deepSetValue(prebidResponseBidObject, 'meta.impid', openRTBResponseBidObject.impid); + deepSetValue(prebidResponseBidObject, 'meta.cat', openRTBResponseBidObject.cat); + deepSetValue(prebidResponseBidObject, 'meta.cid', openRTBResponseBidObject.cid); +} + +function hasValidMediaType(bidRequest) { + const supported = hasBannerMediaType(bidRequest) || hasVideoMediaType(bidRequest) + if (!supported) { + logError('AIDEM Bid Adapter: media type not supported', { bidder: BIDDER_CODE, code: ERROR_CODES.MEDIA_TYPE_NOT_SUPPORTED }); + } + return supported +} + +function hasBannerMediaType(bidRequest) { + return !!deepAccess(bidRequest, 'mediaTypes.banner') +} + +function hasVideoMediaType(bidRequest) { + return !!deepAccess(bidRequest, 'mediaTypes.video') +} + +function hasValidBannerMediaType(bidRequest) { + const sizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes') + if (!sizes) { + logError('AIDEM Bid Adapter: media type sizes missing', { bidder: BIDDER_CODE, code: ERROR_CODES.PROPERTY_NOT_INCLUDED }); + return false; + } + return true +} + +function hasValidVideoMediaType(bidRequest) { + const sizes = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + if (!sizes) { + logError('AIDEM Bid Adapter: media type playerSize missing', { bidder: BIDDER_CODE, code: ERROR_CODES.PROPERTY_NOT_INCLUDED }); + return false; + } + return true +} + +function hasValidVideoParameters(bidRequest) { + let valid = true + const adUnitsParameters = deepAccess(bidRequest, 'mediaTypes.video'); + const bidderParameter = deepAccess(bidRequest, 'params.video'); + for (let property of REQUIRED_VIDEO_PARAMS) { + const hasAdUnitParameter = adUnitsParameters.hasOwnProperty(property) + const hasBidderParameter = bidderParameter && bidderParameter.hasOwnProperty(property) + if (!hasAdUnitParameter && !hasBidderParameter) { + logError(`AIDEM Bid Adapter: ${property} is not included in either the adunit or params level`, { bidder: BIDDER_CODE, code: ERROR_CODES.PROPERTY_NOT_INCLUDED }); + valid = false + } + } + + return valid +} + +function hasValidParameters(bidRequest) { + // Assigned from AIDEM to a publisher website + const siteId = deepAccess(bidRequest, 'params.siteId'); + const publisherId = deepAccess(bidRequest, 'params.publisherId'); + + if (!isStr(siteId)) { + logError('AIDEM Bid Adapter: siteId must valid string', { bidder: BIDDER_CODE, code: ERROR_CODES.SITE_ID_INVALID_VALUE }); + return false; + } + + if (!isStr(publisherId)) { + logError('AIDEM Bid Adapter: publisherId must valid string', { bidder: BIDDER_CODE, code: ERROR_CODES.PUBLISHER_ID_INVALID_VALUE }); + return false; + } + + return true +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: SUPPORTED_MEDIA_TYPES, + isBidRequestValid: function(bidRequest) { + logInfo('bid: ', bidRequest); + + // check if request has valid mediaTypes + if (!hasValidMediaType(bidRequest)) return false + + // check if request has valid media type parameters at adUnit level + if (hasBannerMediaType(bidRequest) && !hasValidBannerMediaType(bidRequest)) { + return false + } + + if (hasVideoMediaType(bidRequest) && !hasValidVideoMediaType(bidRequest)) { + return false + } + + if (hasVideoMediaType(bidRequest) && !hasValidVideoParameters(bidRequest)) { + return false + } + + return hasValidParameters(bidRequest) + }, + + buildRequests: function(validBidRequests, bidderRequest) { + logInfo('validBidRequests: ', validBidRequests); + logInfo('bidderRequest: ', bidderRequest); + const prebidRequest = getPrebidRequestFields(bidderRequest, validBidRequests) + const payloadString = JSON.stringify(prebidRequest); + + return { + method: 'POST', + url: endpoints.request, + data: payloadString, + options: { + withCredentials: true + } + }; + }, + + interpretResponse: function (serverResponse) { + const bids = []; + logInfo('serverResponse: ', serverResponse); + _each(serverResponse.body.bid, function (bidObject) { + logInfo('bidObject: ', bidObject); + if (!bidObject.price || !bidObject.adm) { + return; + } + logInfo('CPM OK'); + const bid = getPrebidResponseBidObject(bidObject) + bids.push(bid); + }); + return bids; + }, + + onBidWon: function(bid) { + // Bidder specific code + logInfo('onBidWon bid: ', bid); + const notice = buildWinNotice(bid) + ajax(endpoints.notice.win, null, JSON.stringify(notice), { method: 'POST', withCredentials: true }); + }, + + onBidderError: function({ bidderRequest }) { + // Bidder specific code + const notice = buildErrorNotice(bidderRequest) + ajax(endpoints.notice.error, null, JSON.stringify(notice), { method: 'POST', withCredentials: true }); + }, +} +registerBidder(spec); diff --git a/modules/aidemBidAdapter.md b/modules/aidemBidAdapter.md new file mode 100644 index 00000000000..a5beca97d10 --- /dev/null +++ b/modules/aidemBidAdapter.md @@ -0,0 +1,187 @@ +# Overview + +``` +name: AIDEM Adapter +type: Bidder Adapter +support: prebid@aidem.com +biddercode: aidem +``` + +# Description +This module connects publishers to AIDEM demand. + +This module is GDPR and CCPA compliant, and no 3rd party userIds are allowed. + + +## Global Bid Params +| Name | Scope | Description | Example | Type | +|---------------|----------|---------------------|---------------|----------| +| `siteId` | required | Unique site ID | `'ABCDEF'` | `String` | +| `publisherId` | required | Unique publisher ID | `'ABCDEF'` | `String` | + + +### Banner Bid Params +| Name | Scope | Description | Example | Type | +|------------|----------|--------------------------|---------------------------|---------| +| `sizes` | required | List of the sizes wanted | `[[300, 250], [300,600]]` | `Array` | + + +### Video Bid Params +| Name | Scope | Description | Example | Type | +|---------------|----------|-----------------------------------------|-----------------|-----------| +| `context` | required | One of instream, outstream, adpod | `'instream'` | `String` | +| `playerSize` | required | Width and height of the player | `'[640, 480]'` | `Array` | +| `maxduration` | required | Maximum video ad duration, in seconds | `30` | `Integer` | +| `minduration` | required | Minimum video ad duration, in seconds | `5` | `Integer` | +| `mimes` | required | List of the content MIME types supported by the player | `["video/mp4"]` | `Array` | +| `protocols` | required | An array of supported video protocols. At least one supported protocol must be specified, where: `2` = VAST 2.0 `3` = VAST 3.0 `5` = VAST 2.0 wrapper `6` = VAST 3.0 wrapper | `2` | `Array` | + + +### Additional Config +| Name | Scope | Description | Example | Type | +|---------------------|----------|---------------------------------------------------------|---------|-----------| +| `coppa` | optional | Child Online Privacy Protection Act | `true` | `Boolean` | +| `consentManagement` | optional | [Consent Management Object](#consent-management-object) | `{}` | `Object` | + + +### Consent Management Object +| Name | Scope | Description | Example | Type | +|--------|----------|--------------------------------------------------------------------------------------------------|---------|----------| +| `gdpr` | optional | GDPR Object see [Prebid.js doc](https://docs.prebid.org/dev-docs/modules/consentManagement.html) | `{}` | `Object` | +| `usp` | optional | USP Object see [Prebid.js doc](https://docs.prebid.org/dev-docs/modules/consentManagementUsp.html) | `{}` | `Object` | + + +### Example Banner ad unit +```javascript +var adUnits = [{ + code: 'banner-prebid-test-site', + mediaTypes: { + banner: { + sizes: [ + [300, 600], + [300, 250] + ] + } + }, + bids: [{ + bidder: 'aidem', + params: { + siteId: 'prebid-test-site', + }, + }] +}]; +``` + +### Example Video ad unit +```javascript +var adUnits = [{ + code: 'video-prebid-test-site', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + maxduration: 30, + minduration: 5, + mimes: ["video/mp4"], + protocols: 2 + } + }, + bids: [{ + bidder: 'aidem', + params: { + siteId: 'prebid-test-site', + }, + }] +}]; +``` + +### Example GDPR Consent Management +```javascript +var pbjs = pbjs || {}; +pbjs.que = pbjs.que || []; + +pbjs.que.push(function (){ + pbjs.setConfig({ + consentManagement: { + gdpr:{ + cmpApi: 'iab' + } + } + }); +}) +``` + + +### Example USP Consent Management +```javascript +var pbjs = pbjs || {}; +pbjs.que = pbjs.que || []; + +pbjs.que.push(function (){ + pbjs.setConfig({ + consentManagement: { + usp:{ + cmpApi: 'static', + consentData:{ + getUSPData:{ + uspString: '1YYY' + } + } + } + } + }); +}) +``` + + +### Setting First Party Data (FPD) +```javascript +var pbjs = pbjs || {}; +pbjs.que = pbjs.que || []; + +pbjs.que.push(function (){ + pbjs.setConfig({ + ortb2: { + site: { + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + keywords: 'power tools, drills' + }, + } + }); +}) +``` + +### Supported Media Types +| Type | Support | +|--------|--------------------------------------------------------------------| +| Banner | Support all [AIDEM Sizes](https://kb.aidem.com/ssp/lists/adsizes/) | +| Video | Support all [AIDEM Sizes](https://kb.aidem.com/ssp/lists/adsizes/) | + + +# Setup / Dev Guide +```shell +nvm use + +npm install + +gulp build --modules=aidemBidAdapter + +gulp serve --modules=aidemBidAdapter + +# Open a chrome browser with no ad blockers enabled, and paste in this URL. The `pbjs_debug=true` is needed if you want to enable `loggerInfo` output on the `console` tab of Chrome Developer Tools. +http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true +``` + +If you need to run the tests suite but do *not* want to have to build the full adapter and serve it, simply run: +```shell +gulp test --file "test/spec/modules/aidemBidAdapter_spec.js" +``` + + +For video: gulp serve --modules=aidemBidAdapter,dfpAdServerVideo + +# FAQs +### How do I view AIDEM bid request? +Navigate to a page where AIDEM is setup to bid. In the network tab, +search for requests to `zero.aidemsrv.com/bid/request`. diff --git a/test/spec/modules/aidemBidAdapter_spec.js b/test/spec/modules/aidemBidAdapter_spec.js new file mode 100644 index 00000000000..472b226ab8a --- /dev/null +++ b/test/spec/modules/aidemBidAdapter_spec.js @@ -0,0 +1,664 @@ +import {expect} from 'chai'; +import {setEndPoints, spec} from 'modules/aidemBidAdapter.js'; +import * as utils from '../../../src/utils'; +import {deepSetValue} from '../../../src/utils'; +import {server} from '../../mocks/xhr'; +import {config} from '../../../src/config'; +import {NATIVE} from '../../../src/mediaTypes.js'; + +// Full banner + Full Video + Basic Banner + Basic Video +const VALID_BIDS = [ + { + bidder: 'aidem', + params: { + siteId: '301491', + publisherId: '3021491', + placementId: 13144370, + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + }, + { + bidder: 'aidem', + params: { + siteId: '301491', + publisherId: '3021491', + placementId: 13144370, + }, + mediaTypes: { + video: { + context: 'instream', + minduration: 7, + maxduration: 30, + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [2] + } + }, + }, +] + +const INVALID_BIDS = [ + { + bidder: 'aidem', + params: { + siteId: '3014912' + } + }, + { + bidder: 'aidem', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + params: { + siteId: '3014912', + } + }, + { + bidder: 'aidem', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + params: { + publisherId: '3014912', + } + }, + { + bidder: 'aidem', + params: { + siteId: '3014912', + member: '301e4912' + } + }, + { + bidder: 'aidem', + params: { + siteId: '3014912', + invCode: '3014912' + } + }, + { + bidder: 'aidem', + mediaType: NATIVE, + params: { + siteId: '3014912' + } + }, + { + bidder: 'aidem', + mediaTypes: { + banner: {} + }, + }, + { + bidder: 'aidem', + mediaTypes: { + video: { + placement: 1, + minduration: 7, + maxduration: 30, + mimes: ['video/mp4'], + protocols: [2] + } + }, + params: { + siteId: '301491', + placementId: 13144370, + }, + }, + { + bidder: 'aidem', + mediaTypes: { + video: { + minduration: 7, + maxduration: 30, + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [2] + } + }, + params: { + siteId: '301491', + placementId: 13144370, + }, + }, + { + bidder: 'aidem', + mediaTypes: { + video: { + minduration: 7, + maxduration: 30, + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [2], + placement: 1 + } + }, + params: { + siteId: '301491', + placementId: 13144370, + video: { + size: [480, 40] + } + }, + }, +] + +const DEFAULT_VALID_BANNER_REQUESTS = [ + { + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'aidem', + bidderRequestId: '15246a574e859f', + mediaTypes: { + banner: { + sizes: [ + [ 300, 250 ], + [ 300, 600 ] + ] + } + }, + params: { + siteId: 1, + placementId: 13144370 + }, + src: 'client', + transactionId: '54a58774-7a41-494e-9aaf-fa7b79164f0c' + } +]; + +const DEFAULT_VALID_VIDEO_REQUESTS = [ + { + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'aidem', + bidderRequestId: '15246a574e859f', + mediaTypes: { + video: { + minduration: 7, + maxduration: 30, + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [2] + } + }, + params: { + siteId: 1, + placementId: 13144370 + }, + src: 'client', + transactionId: '54a58774-7a41-494e-9aaf-fa7b79164f0c' + } +]; + +const VALID_BIDDER_REQUEST = { + auctionId: '6e9b46c3-65a8-46ea-89f4-c5071110c85c', + bidderCode: 'aidem', + bidderRequestId: '170ea5d2b1d073', + refererInfo: { + page: 'test-page', + domain: 'test-domain', + ref: 'test-referer' + }, +} + +// Add mediatype +const SERVER_RESPONSE_BANNER = { + body: { + id: 'efa1930a-bc3e-4fd0-8368-08bc40236b4f', + bid: [ + // BANNER + { + 'id': '2e614be960ee1d', + 'impid': '2e614be960ee1d', + 'price': 7.91, + 'mediatype': 'banner', + 'adid': '24277955', + 'adm': 'creativity_banner', + 'adomain': [ + 'aidem.com' + ], + 'iurl': 'http://www.aidem.com', + 'cat': [], + 'cid': '4193561', + 'crid': '24277955', + 'w': 300, + 'h': 250, + 'ext': { + 'dspid': 85, + 'advbrandid': 1246, + 'advbrand': 'AIDEM' + } + }, + ], + cur: 'USD' + }, +} + +const SERVER_RESPONSE_VIDEO = { + body: { + id: 'efa1930a-bc3e-4fd0-8368-08bc40236b4f', + bid: [ + // VIDEO + { + 'id': '2876a29392a47c', + 'impid': '2876a29392a47c', + 'price': 7.93, + 'mediatype': 'video', + 'adid': '24277955', + 'adm': 'https://hermes.aidemsrv.com/vast-tag/cl9mzhhd502uq09l720uegb02?auction_id={{AUCTION_ID}}&cachebuster={{CACHEBUSTER}}', + 'adomain': [ + 'aidem.com' + ], + 'iurl': 'http://www.aidem.com', + 'cat': [], + 'cid': '4193561', + 'crid': '24277955', + 'w': 640, + 'h': 480, + 'ext': { + 'dspid': 85, + 'advbrandid': 1246, + 'advbrand': 'AIDEM' + } + } + ], + cur: 'USD' + }, +} + +const WIN_NOTICE = { + 'adId': '3a20ee5dc78c1e', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'creativeId': '24277955', + 'cpm': 1, + 'netRevenue': false, + 'adserverTargeting': { + 'hb_bidder': 'aidem', + 'hb_adid': '3a20ee5dc78c1e', + 'hb_pb': '1.00', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'tenutabene.it' + }, + 'auctionId': '85864730-6cbc-4e56-bc3c-a4a6596dca5b', + 'currency': [ + 'USD' + ], + 'mediaType': 'banner', + 'size': '300x250', + 'width': 300, + 'height': 250, + 'status': 'rendered', + 'transactionId': 'ce089116-4251-45c3-bdbb-3a03cb13816b', + 'ttl': 300, + 'requestTimestamp': 1666796241007, + 'responseTimestamp': 1666796241021, + metrics: { + getMetrics() { + return { + + } + } + } +} + +const ERROR_NOTICE = { + 'message': 'Prebid.js: Server call for aidem failed.', + 'url': 'http%3A%2F%2Flocalhost%3A9999%2FintegrationExamples%2Fgpt%2Fhello_world.html%3Fpbjs_debug%3Dtrue', + 'auctionId': 'b57faab7-23f7-4b63-90db-67b259d20db7', + 'bidderRequestId': '1c53857d1ce616', + 'timeout': 1000, + 'bidderCode': 'aidem', + metrics: { + getMetrics() { + return { + + } + } + } +} + +describe('Aidem adapter', () => { + describe('isBidRequestValid', () => { + it('should return true for each valid bid requests', function () { + // spec.isBidRequestValid() + VALID_BIDS.forEach((value, index) => { + expect(spec.isBidRequestValid(value)).to.be.true + }) + }); + + it('should return false for each invalid bid requests', function () { + // spec.isBidRequestValid() + INVALID_BIDS.forEach((value, index) => { + expect(spec.isBidRequestValid(value)).to.be.false + }) + }); + + it('should return true if valid banner sizes are specified in params as single array', function () { + // spec.isBidRequestValid() + const validBannerRequest = utils.deepClone(VALID_BIDS[0]) + deepSetValue(validBannerRequest.params, 'banner.size', [300, 250]) + expect(spec.isBidRequestValid(validBannerRequest)).to.be.true + }); + + it('should return true if valid banner sizes are specified in params as array of array', function () { + // spec.isBidRequestValid() + const validBannerRequest = utils.deepClone(VALID_BIDS[0]) + deepSetValue(validBannerRequest.params, 'banner.size', [[300, 600]]) + expect(spec.isBidRequestValid(validBannerRequest)).to.be.true + }); + + it('should return true if valid video sizes are specified in params as single array', function () { + // spec.isBidRequestValid() + const validVideoRequest = utils.deepClone(VALID_BIDS[1]) + deepSetValue(validVideoRequest.params, 'video.size', [640, 480]) + expect(spec.isBidRequestValid(validVideoRequest)).to.be.true + }); + }); + + describe('buildRequests', () => { + it('should match server requirements', () => { + const requests = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + expect(requests).to.be.an('object'); + expect(requests.method).to.be.a('string') + expect(requests.data).to.be.a('string') + expect(requests.options).to.be.an('object').that.have.a.property('withCredentials') + }); + + it('should have a well formatted banner payload', () => { + const requests = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const payload = JSON.parse(requests.data) + expect(payload).to.be.a('object').that.has.all.keys( + 'id', 'imp', 'device', 'cur', 'tz', 'regs', 'site', 'environment', 'at' + ) + expect(payload.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_BANNER_REQUESTS.length) + + expect(payload.imp[0]).to.be.a('object').that.has.all.keys( + 'banner', 'id', 'mediatype', 'imp_ext', 'tid' + ) + expect(payload.imp[0].banner).to.be.a('object').that.has.all.keys( + 'format', 'topframe' + ) + }); + + it('should have a well formatted video payload', () => { + const requests = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST); + const payload = JSON.parse(requests.data) + expect(payload).to.be.a('object').that.has.all.keys( + 'id', 'imp', 'device', 'cur', 'tz', 'regs', 'site', 'environment', 'at' + ) + expect(payload.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_VIDEO_REQUESTS.length) + + expect(payload.imp[0]).to.be.a('object').that.has.all.keys( + 'video', 'id', 'mediatype', 'imp_ext', 'tid' + ) + expect(payload.imp[0].video).to.be.a('object').that.has.all.keys( + 'format', 'mimes', 'minDuration', 'maxDuration', 'protocols' + ) + }); + + it('should have a well formatted bid floor payload if configured', () => { + const validBannerRequests = utils.deepClone(DEFAULT_VALID_BANNER_REQUESTS) + validBannerRequests[0].params.floor = { + value: 1.98, + currency: 'USD' + } + const requests = spec.buildRequests(validBannerRequests, VALID_BIDDER_REQUEST); + const payload = JSON.parse(requests.data) + const { floor } = payload.imp[0] + expect(floor).to.be.a('object').that.has.all.keys( + 'value', 'currency' + ) + }); + }) + + describe('interpretResponse', () => { + it('should return a valid bid array with a banner bid', () => { + const response = utils.deepClone(SERVER_RESPONSE_BANNER) + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(1) + interpreted.forEach(value => { + expect(value).to.be.a('object').that.has.all.keys( + 'ad', 'cpm', 'creativeId', 'currency', 'height', 'mediaType', 'meta', 'netRevenue', 'requestId', 'ttl', 'width', 'dealId' + ) + }) + }); + + it('should return a valid bid array with a banner bid', () => { + const response = utils.deepClone(SERVER_RESPONSE_VIDEO) + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(1) + interpreted.forEach(value => { + expect(value).to.be.a('object').that.has.all.keys( + 'vastUrl', 'cpm', 'creativeId', 'currency', 'height', 'mediaType', 'meta', 'netRevenue', 'requestId', 'ttl', 'width', 'dealId' + ) + }) + }); + + it('should return a valid bid array with netRevenue', () => { + const response = utils.deepClone(SERVER_RESPONSE_BANNER) + response.body.bid[0].isNet = true + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(1) + expect(interpreted[0].netRevenue).to.be.true + }); + + it('should return an empty bid array if one of seatbid entry is missing price property', () => { + const response = utils.deepClone(SERVER_RESPONSE_BANNER) + delete response.body.bid[0].price + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(0) + }); + + it('should return an empty bid array if one of seatbid entry is missing adm property', () => { + const response = utils.deepClone(SERVER_RESPONSE_BANNER) + delete response.body.bid[0].adm + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(0) + }); + }) + + describe('onBidWon', () => { + it(`should exists and type function`, function () { + expect(spec.onBidWon).to.exist.and.to.be.a('function') + }); + + it(`should send a valid bid won notice`, function () { + spec.onBidWon(WIN_NOTICE); + // server.respondWith('POST', WIN_EVENT_URL, [ + // 400, {'Content-Type': 'application/json'}, ) + // ]); + expect(server.requests.length).to.equal(1); + }); + }); + + describe('onBidderError', () => { + it(`should exists and type function`, function () { + expect(spec.onBidderError).to.exist.and.to.be.a('function') + }); + + it(`should send a valid error notice`, function () { + spec.onBidderError({ bidderRequest: ERROR_NOTICE }) + expect(server.requests.length).to.equal(1); + const body = JSON.parse(server.requests[0].requestBody) + expect(body).to.be.a('object').that.has.all.keys('message', 'auctionId', 'bidderRequestId', 'url', 'metrics') + // const { bids } = JSON.parse(server.requests[0].requestBody) + // expect(bids).to.be.a('array').that.has.lengthOf(1) + // _each(bids, (bid) => { + // expect(bid).to.be.a('object').that.has.all.keys('adUnitCode', 'auctionId', 'bidId', 'bidderRequestId', 'transactionId', 'metrics') + // }) + }); + }); + + describe('setEndPoints', () => { + it(`should exists and type function`, function () { + expect(setEndPoints).to.exist.and.to.be.a('function') + }); + + it(`should not modify default endpoints`, function () { + const endpoints = setEndPoints() + const requestURL = new URL(endpoints.request) + const winNoticeURL = new URL(endpoints.notice.win) + const timeoutNoticeURL = new URL(endpoints.notice.timeout) + const errorNoticeURL = new URL(endpoints.notice.error) + + expect(requestURL.host).to.equal('zero.aidemsrv.com') + expect(winNoticeURL.host).to.equal('zero.aidemsrv.com') + expect(timeoutNoticeURL.host).to.equal('zero.aidemsrv.com') + expect(errorNoticeURL.host).to.equal('zero.aidemsrv.com') + + expect(decodeURIComponent(requestURL.pathname)).to.equal('/bid/request') + expect(decodeURIComponent(winNoticeURL.pathname)).to.equal('/notice/win') + expect(decodeURIComponent(timeoutNoticeURL.pathname)).to.equal('/notice/timeout') + expect(decodeURIComponent(errorNoticeURL.pathname)).to.equal('/notice/error') + }); + + it(`should not change request endpoint`, function () { + const endpoints = setEndPoints('default') + const requestURL = new URL(endpoints.request) + expect(decodeURIComponent(requestURL.pathname)).to.equal('/bid/request') + }); + + it(`should change to local env`, function () { + const endpoints = setEndPoints('local') + const requestURL = new URL(endpoints.request) + const winNoticeURL = new URL(endpoints.notice.win) + const timeoutNoticeURL = new URL(endpoints.notice.timeout) + const errorNoticeURL = new URL(endpoints.notice.error) + + expect(requestURL.host).to.equal('127.0.0.1:8787') + expect(winNoticeURL.host).to.equal('127.0.0.1:8787') + expect(timeoutNoticeURL.host).to.equal('127.0.0.1:8787') + expect(errorNoticeURL.host).to.equal('127.0.0.1:8787') + }); + + it(`should add a path prefix`, function () { + const endpoints = setEndPoints('local', '/path') + const requestURL = new URL(endpoints.request) + const winNoticeURL = new URL(endpoints.notice.win) + const timeoutNoticeURL = new URL(endpoints.notice.timeout) + const errorNoticeURL = new URL(endpoints.notice.error) + + expect(decodeURIComponent(requestURL.pathname)).to.equal('/path/bid/request') + expect(decodeURIComponent(winNoticeURL.pathname)).to.equal('/path/notice/win') + expect(decodeURIComponent(timeoutNoticeURL.pathname)).to.equal('/path/notice/timeout') + expect(decodeURIComponent(errorNoticeURL.pathname)).to.equal('/path/notice/error') + }); + + it(`should add a path prefix and change request endpoint`, function () { + const endpoints = setEndPoints('local', '/path') + const requestURL = new URL(endpoints.request) + const winNoticeURL = new URL(endpoints.notice.win) + const timeoutNoticeURL = new URL(endpoints.notice.timeout) + const errorNoticeURL = new URL(endpoints.notice.error) + + expect(decodeURIComponent(requestURL.pathname)).to.equal('/path/bid/request') + expect(decodeURIComponent(winNoticeURL.pathname)).to.equal('/path/notice/win') + expect(decodeURIComponent(timeoutNoticeURL.pathname)).to.equal('/path/notice/timeout') + expect(decodeURIComponent(errorNoticeURL.pathname)).to.equal('/path/notice/error') + }); + }); + + describe('config', () => { + beforeEach(() => { + config.setConfig({ + aidem: { + env: 'main' + } + }); + }) + + it(`should not override default endpoints`, function () { + config.setConfig({ + aidem: { + env: 'unknown', + path: '/test' + } + }); + const { url } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const requestURL = new URL(url) + expect(requestURL.host).to.equal('zero.aidemsrv.com') + }); + + it(`should set local endpoints`, function () { + config.setConfig({ + aidem: { + env: 'local', + path: '/test' + } + }); + const { url } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const requestURL = new URL(url) + expect(requestURL.host).to.equal('127.0.0.1:8787') + }); + + it(`should set coppa`, function () { + config.setConfig({ + coppa: true + }); + const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const request = JSON.parse(data) + expect(request.regs.coppa_applies).to.equal(true) + }); + + it(`should set gdpr to true`, function () { + config.setConfig({ + consentManagement: { + gdpr: { + // any data here set gdpr to true + }, + } + }); + const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const request = JSON.parse(data) + expect(request.regs.gdpr_applies).to.equal(true) + }); + + it(`should set usp_consent string`, function () { + config.setConfig({ + consentManagement: { + usp: { + cmpApi: 'static', + consentData: { + getUSPData: { + uspString: '1YYY' + } + } + } + } + }); + const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const request = JSON.parse(data) + expect(request.regs.us_privacy).to.equal('1YYY') + }); + + it(`should not set usp_consent string`, function () { + config.setConfig({ + consentManagement: { + usp: { + cmpApi: 'iab', + consentData: { + getUSPData: { + uspString: '1YYY' + } + } + } + } + }); + const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const request = JSON.parse(data) + expect(request.regs.us_privacy).to.undefined + }); + }); +}); From 22bf423e28b74674ecd89e7fc3f48473487f33dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20Kok=C5=82owski?= <38455696+skoklowski@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:06:31 +0100 Subject: [PATCH 477/569] Ringier Axel Springer Bidder Adapter (#9239) - New parameter `customParams` Co-authored-by: skoklowski --- modules/rasBidAdapter.js | 11 ++++++--- modules/rasBidAdapter.md | 31 +++++++++++++------------ test/spec/modules/rasBidAdapter_spec.js | 6 ++++- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/modules/rasBidAdapter.js b/modules/rasBidAdapter.js index 7bc3cf66b0d..f8ca260d490 100644 --- a/modules/rasBidAdapter.js +++ b/modules/rasBidAdapter.js @@ -1,4 +1,3 @@ -import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { isEmpty, getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; @@ -12,9 +11,15 @@ const getEndpoint = (network) => { function parseParams(params, bidderRequest) { const newParams = {}; + if (params.customParams && typeof params.customParams === 'object') { + for (const param in params.customParams) { + if (params.customParams.hasOwnProperty(param)) { + newParams[param] = params.customParams[param]; + } + } + } const du = deepAccess(bidderRequest, 'refererInfo.page'); const dr = deepAccess(bidderRequest, 'refererInfo.ref'); - if (du) { newParams.du = du; } @@ -93,7 +98,7 @@ const getSlots = (bidRequests) => { const batchSize = bidRequests.length; for (let i = 0; i < batchSize; i++) { const adunit = bidRequests[i]; - const slotSequence = utils.deepAccess(adunit, 'params.slotSequence'); + const slotSequence = deepAccess(adunit, 'params.slotSequence'); const sizes = parseSizesInput(getAdUnitSizes(adunit)).join(','); diff --git a/modules/rasBidAdapter.md b/modules/rasBidAdapter.md index 384ba0b611f..e8a61974130 100644 --- a/modules/rasBidAdapter.md +++ b/modules/rasBidAdapter.md @@ -34,18 +34,19 @@ var adUnits = [{ # Parameters -| Name | Scope | Type | Description | Example -| --- | --- | --- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- -| network | required | String | Specific identifier provided by RAS | `"4178463"` -| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by RAS | `"example_com"` -| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` -| slot | required | String | Ad unit placement name (case-insensitive) provided by RAS | `"slot"` -| slotSequence | optional | Number | Ad unit sequence position provided by RAS | `1` -| pageContext | optional | Object | Web page context data | `{}` -| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` -| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` -| pageContext.dv | optional | String | Document virtual address as slash-separated path that may consist of any number of parts (case-insensitive alphanumeric with underscores and hyphens); first part should be the same as `site` value and second as `area` value; next parts may reflect website navigation | `"example_com/sport/football"` -| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` -| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` -| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` -| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` +| Name | Scope | Type | Description | Example | +|------------------------------|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| network | required | String | Specific identifier provided by RAS | `"4178463"` | +| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by RAS | `"example_com"` | +| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` | +| slot | required | String | Ad unit placement name (case-insensitive) provided by RAS | `"slot"` | +| slotSequence | optional | Number | Ad unit sequence position provided by RAS | `1` | +| pageContext | optional | Object | Web page context data | `{}` | +| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` | +| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` | +| pageContext.dv | optional | String | Document virtual address as slash-separated path that may consist of any number of parts (case-insensitive alphanumeric with underscores and hyphens); first part should be the same as `site` value and second as `area` value; next parts may reflect website navigation | `"example_com/sport/football"` | +| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` | +| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` | +| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` | +| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` | +| customParams | optional | Object | Custom request params | `{}` | \ No newline at end of file diff --git a/test/spec/modules/rasBidAdapter_spec.js b/test/spec/modules/rasBidAdapter_spec.js index 8c378aaa416..bfa72a2510e 100644 --- a/test/spec/modules/rasBidAdapter_spec.js +++ b/test/spec/modules/rasBidAdapter_spec.js @@ -59,7 +59,10 @@ describe('rasBidAdapter', function () { area: 'areatest', site: 'test', slotSequence: '0', - network: '4178463' + network: '4178463', + customParams: { + test: 'name=value' + } } }; const bid2 = { @@ -98,6 +101,7 @@ describe('rasBidAdapter', function () { expect(requests[0].url).to.have.string('euconsent=some-consent-string'); expect(requests[0].url).to.have.string('du=https%3A%2F%2Fexample.com%2F'); expect(requests[0].url).to.have.string('dr=https%3A%2F%2Fexample.org%2F'); + expect(requests[0].url).to.have.string('test=name%3Dvalue'); }); it('should return empty consent string when undefined', function () { From 54b6e7fb9ef2da399b3c95a03891db4b4e51f8fd Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Mon, 28 Nov 2022 09:09:16 -0800 Subject: [PATCH 478/569] updated ref info page logic (#9241) --- src/refererDetection.js | 7 ++- test/spec/modules/enrichmentFpdModule_spec.js | 5 +- test/spec/modules/fpdModule_spec.js | 2 +- test/spec/refererDetection_spec.js | 52 ++++++++++++++++--- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/refererDetection.js b/src/refererDetection.js index 15c080f5c69..e0cb15522cc 100644 --- a/src/refererDetection.js +++ b/src/refererDetection.js @@ -120,6 +120,7 @@ export function detectReferer(win) { const stack = []; const ancestors = getAncestorOrigins(win); const maxNestedIframes = config.getConfig('maxNestedIframes'); + let currentWindow; let bestLocation; let bestCanonicalUrl; @@ -226,7 +227,11 @@ export function detectReferer(win) { const location = reachedTop || hasTopLocation ? bestLocation : null; const canonicalUrl = config.getConfig('pageUrl') || bestCanonicalUrl || null; - const page = ensureProtocol(canonicalUrl, win) || location; + let page = config.getConfig('pageUrl') || location || ensureProtocol(canonicalUrl, win); + + if (location && location.indexOf('?') > -1 && page.indexOf('?') === -1) { + page = `${page}${location.substring(location.indexOf('?'))}`; + } return { reachedTop, diff --git a/test/spec/modules/enrichmentFpdModule_spec.js b/test/spec/modules/enrichmentFpdModule_spec.js index 1c6cab5afec..38853f1528c 100644 --- a/test/spec/modules/enrichmentFpdModule_spec.js +++ b/test/spec/modules/enrichmentFpdModule_spec.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import {config} from 'src/config.js'; import { getRefererInfo } from 'src/refererDetection.js'; import {processFpd, coreStorage, resetEnrichments} from 'modules/enrichmentFpdModule.js'; import * as enrichmentModule from 'modules/enrichmentFpdModule.js'; @@ -77,10 +78,10 @@ describe('the first party data enrichment module', function() { expect(validated.site.keywords).to.be.undefined; }); - it('adds page domain values if canonical url exists', function() { + it('adds page domain values if pageUrl url exists', function() { + config.setConfig({'pageUrl': 'https://www.subdomain.domain.co.uk/path?query=12345'}); width = 800; height = 500; - canonical.href = 'https://www.subdomain.domain.co.uk/path?query=12345'; let validated = syncProcessFpd({}, {}).global; diff --git a/test/spec/modules/fpdModule_spec.js b/test/spec/modules/fpdModule_spec.js index 8e95f462830..7d41fbb55e1 100644 --- a/test/spec/modules/fpdModule_spec.js +++ b/test/spec/modules/fpdModule_spec.js @@ -130,7 +130,7 @@ describe('the first party data module', function () { } }; - canonical.href = 'https://www.domain.com/path?query=12345'; + config.setConfig({'pageUrl': 'https://www.domain.com/path?query=12345'}); width = 1120; height = 750; diff --git a/test/spec/refererDetection_spec.js b/test/spec/refererDetection_spec.js index 3f33d1646a1..f8166a27d85 100644 --- a/test/spec/refererDetection_spec.js +++ b/test/spec/refererDetection_spec.js @@ -128,11 +128,49 @@ describe('Referer detection', () => { numIframes: 0, stack: ['https://example.com/some/page'], canonicalUrl: 'https://example.com/canonical/page', - page: 'https://example.com/canonical/page', + page: 'https://example.com/some/page', ref: 'https://othersite.com/', domain: 'example.com' }); }); + + it('Should set page and canonical to pageUrl value set in config if present, even if canonical url is also present in head', () => { + config.setConfig({'pageUrl': 'https://www.set-from-config.com/path'}); + const testWindow = buildWindowTree(['https://example.com/some/page'], 'https://othersite.com/', 'https://example.com/canonical/page'), + result = detectReferer(testWindow)(); + + sinon.assert.match(result, { + topmostLocation: 'https://example.com/some/page', + location: 'https://example.com/some/page', + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: ['https://example.com/some/page'], + canonicalUrl: 'https://www.set-from-config.com/path', + page: 'https://www.set-from-config.com/path', + ref: 'https://othersite.com/', + domain: 'www.set-from-config.com' + }); + }); + + it('Should set page with query params if canonical url is present without query params but the current page does have them', () => { + config.setConfig({'pageUrl': 'https://www.set-from-config.com/path'}); + const testWindow = buildWindowTree(['https://example.com/some/page?query1=123&query2=456'], 'https://othersite.com/', 'https://example.com/canonical/page'), + result = detectReferer(testWindow)(); + + sinon.assert.match(result, { + topmostLocation: 'https://example.com/some/page?query1=123&query2=456', + location: 'https://example.com/some/page?query1=123&query2=456', + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: ['https://example.com/some/page?query1=123&query2=456'], + canonicalUrl: 'https://www.set-from-config.com/path', + page: 'https://www.set-from-config.com/path?query1=123&query2=456', + ref: 'https://othersite.com/', + domain: 'www.set-from-config.com' + }); + }); }); describe('Friendly iframes', () => { @@ -174,7 +212,7 @@ describe('Referer detection', () => { 'https://example.com/third/page' ], canonicalUrl: 'https://example.com/canonical/page', - page: 'https://example.com/canonical/page', + page: 'https://example.com/some/page', ref: 'https://othersite.com/', domain: 'example.com' }); @@ -317,7 +355,7 @@ describe('Referer detection', () => { 'https://ad-iframe.ampproject.org/ad' ], canonicalUrl: 'https://example.com/some/page/', - page: 'https://example.com/some/page/', + page: 'https://example.com/some/page/amp/', ref: null, domain: 'example.com' }); @@ -344,7 +382,7 @@ describe('Referer detection', () => { 'https://ad-iframe.ampproject.org/ad' ], canonicalUrl: 'https://example.com/some/page/', - page: 'https://example.com/some/page/', + page: 'https://example.com/some/page/amp/', ref: null, domain: 'example.com' }); @@ -384,7 +422,7 @@ describe('Referer detection', () => { 'https://ad-iframe.ampproject.org/ad' ], canonicalUrl: 'https://example.com/some/page/', - page: 'https://example.com/some/page/', + page: 'https://example.com/some/page/amp/', ref: null, domain: 'example.com', }); @@ -412,7 +450,7 @@ describe('Referer detection', () => { 'https://ad-iframe.ampproject.org/ad' ], canonicalUrl: 'https://example.com/some/page/', - page: 'https://example.com/some/page/', + page: 'https://example.com/some/page/amp/', ref: null, domain: 'example.com' }); @@ -441,7 +479,7 @@ describe('Referer detection', () => { 'https://ad-iframe.ampproject.org/ad' ], canonicalUrl: 'https://example.com/some/page/', - page: 'https://example.com/some/page/', + page: 'https://example.com/some/page/amp/', ref: null, domain: 'example.com', }); From 5a9aaa8ca5bb47393ddfb08b00d1c4e0af5fd850 Mon Sep 17 00:00:00 2001 From: Elad Yosifon Date: Mon, 28 Nov 2022 19:10:23 +0200 Subject: [PATCH 479/569] fix for broken download bundle https://github.com/prebid/prebid.github.io/issues/4177 (#9289) --- modules/{kueezRtbBidAdapter.js => kueezrtbBidAdapter.js} | 0 modules/{kueezRtbBidAdapter.md => kueezrtbBidAdapter.md} | 0 .../{kueezRtbBidAdapter_spec.js => kueezrtbBidAdapter_spec.js} | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename modules/{kueezRtbBidAdapter.js => kueezrtbBidAdapter.js} (100%) rename modules/{kueezRtbBidAdapter.md => kueezrtbBidAdapter.md} (100%) rename test/spec/modules/{kueezRtbBidAdapter_spec.js => kueezrtbBidAdapter_spec.js} (99%) diff --git a/modules/kueezRtbBidAdapter.js b/modules/kueezrtbBidAdapter.js similarity index 100% rename from modules/kueezRtbBidAdapter.js rename to modules/kueezrtbBidAdapter.js diff --git a/modules/kueezRtbBidAdapter.md b/modules/kueezrtbBidAdapter.md similarity index 100% rename from modules/kueezRtbBidAdapter.md rename to modules/kueezrtbBidAdapter.md diff --git a/test/spec/modules/kueezRtbBidAdapter_spec.js b/test/spec/modules/kueezrtbBidAdapter_spec.js similarity index 99% rename from test/spec/modules/kueezRtbBidAdapter_spec.js rename to test/spec/modules/kueezrtbBidAdapter_spec.js index f9b2cd41a43..70ece966651 100644 --- a/test/spec/modules/kueezRtbBidAdapter_spec.js +++ b/test/spec/modules/kueezrtbBidAdapter_spec.js @@ -11,7 +11,7 @@ import { setStorageItem, tryParseJSON, getUniqueDealId, -} from 'modules/kueezRtbBidAdapter.js'; +} from 'modules/kueezrtbBidAdapter.js'; import * as utils from 'src/utils.js'; import {version} from 'package.json'; import {useFakeTimers} from 'sinon'; From 34fd9fbe0c19c94a2b62e2c49f2f16453d36c79e Mon Sep 17 00:00:00 2001 From: Saveliev Taras Date: Mon, 28 Nov 2022 18:21:55 +0100 Subject: [PATCH 480/569] Yandex Bid Adapter: (#9280) * refactoring; * added banner.format to payload; * added tmax support; * fixed nurl sending; * added support for the block identifier format in the Yandex Ad system; Co-authored-by: Taras Saveliev --- modules/yandexBidAdapter.js | 104 +++++++++++++++++---- modules/yandexBidAdapter.md | 14 +-- test/spec/modules/yandexBidAdapter_spec.js | 35 ++++--- 3 files changed, 114 insertions(+), 39 deletions(-) diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index af465453c22..f73b4369869 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -1,5 +1,6 @@ -import { formatQS, deepAccess, triggerPixel } from '../src/utils.js'; +import { formatQS, deepAccess, triggerPixel, isArray, isNumber } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js' const BIDDER_CODE = 'yandex'; const BIDDER_URL = 'https://bs.yandex.ru/metadsp'; @@ -10,9 +11,19 @@ const SSP_ID = 10500; export const spec = { code: BIDDER_CODE, aliases: ['ya'], // short code + supportedMediaTypes: [ BANNER ], isBidRequestValid: function(bid) { - return !!(bid.params && bid.params.pageId && bid.params.impId); + const { params } = bid; + if (!params) { + return false; + } + const { pageId, impId } = extractPlacementIds(params); + if (!(pageId && impId)) { + return false; + } + const sizes = bid.mediaTypes?.banner?.sizes; + return isArray(sizes) && isArray(sizes[0]) && isNumber(sizes[0][0]) && isNumber(sizes[0][1]); }, buildRequests: function(validBidRequests, bidderRequest) { @@ -29,9 +40,19 @@ export const spec = { page = bidderRequest.refererInfo.page; } + let timeout = null; + if (bidderRequest) { + timeout = bidderRequest.timeout; + } + return validBidRequests.map((bidRequest) => { const { params } = bidRequest; - const { pageId, impId, targetRef, withCredentials = true } = params; + const { targetRef, withCredentials = true } = params; + const sizes = bidRequest.mediaTypes.banner.sizes; + const size = sizes[0]; + const [ w, h ] = size; + + const { pageId, impId } = extractPlacementIds(params); const queryParams = { 'imp-id': impId, @@ -43,24 +64,23 @@ export const spec = { queryParams['tcf-consent'] = consentString; } - const floorInfo = bidRequest.getFloor ? bidRequest.getFloor({ - currency: DEFAULT_CURRENCY - }) : {}; - const bidfloor = floorInfo.floor; - const bidfloorcur = floorInfo.currency; - const imp = { id: impId, - bidfloor, - bidfloorcur, - }; - const bannerParams = deepAccess(bidRequest, 'mediaTypes.banner'); - if (bannerParams) { - const [ w, h ] = bannerParams.sizes[0]; - imp.banner = { + banner: { + format: sizes, w, h, - }; + } + }; + + if (bidRequest.getFloor) { + const floorInfo = bidRequest.getFloor({ + currency: DEFAULT_CURRENCY, + size + }); + + imp.bidfloor = floorInfo.floor; + imp.bidfloorcur = floorInfo.currency; } const queryParamsString = formatQS(queryParams); @@ -74,6 +94,7 @@ export const spec = { ref_url: referrer, page_url: page, }, + tmax: timeout, }, options: { withCredentials, @@ -102,6 +123,7 @@ export const spec = { width: rtbBid.w, height: rtbBid.h, creativeId: rtbBid.adid, + nurl: rtbBid.nurl, netRevenue: true, ttl: DEFAULT_TTL, @@ -118,13 +140,57 @@ export const spec = { }, onBidWon: function (bid) { - const nurl = bid['nurl']; - + let nurl = bid['nurl']; if (!nurl) { return; } + + const cpm = deepAccess(bid, 'adserverTargeting.hb_pb') || ''; + const curr = (bid.hasOwnProperty('originalCurrency') && bid.hasOwnProperty('originalCpm')) + ? bid.originalCurrency + : bid.currency; + + nurl = nurl + .replace(/\${AUCTION_PRICE}/, cpm) + .replace(/\${AUCTION_CURRENCY}/, curr) + ; + triggerPixel(nurl); } } +function extractPlacementIds(bidRequestParams) { + const { placementId } = bidRequestParams; + const result = { pageId: null, impId: null }; + + let pageId, impId; + if (placementId) { + /* + * Possible formats + * R-I-123456-2 + * R-123456-1 + * 123456-789 + */ + const num = placementId.lastIndexOf('-'); + if (num === -1) { + return result; + } + const num2 = placementId.lastIndexOf('-', num - 1); + pageId = placementId.slice(num2 + 1, num); + impId = placementId.slice(num + 1); + } else { + pageId = bidRequestParams.pageId; + impId = bidRequestParams.impId; + } + + if (!parseInt(pageId, 10) || !parseInt(impId, 10)) { + return result; + } + + result.pageId = pageId; + result.impId = impId; + + return result; +} + registerBidder(spec); diff --git a/modules/yandexBidAdapter.md b/modules/yandexBidAdapter.md index 7a51d7bc5fb..aee51bca249 100644 --- a/modules/yandexBidAdapter.md +++ b/modules/yandexBidAdapter.md @@ -12,10 +12,11 @@ Yandex Bidder Adapter for Prebid.js. # Parameters -| Name | Scope | Description | Example | Type | -|---------------|----------|-------------------------|-----------|-----------| -| `pageId` | required | Page ID | `123` | `Integer` | -| `impId` | required | Block ID | `1` | `Integer` | +| Name | Required? | Description | Example | Type | +|---------------|--------------------------------------------|-------------|---------|-----------| +| `placementId` | Yes | Block ID | `123-1` | `String` | +| `pageId` | No
Deprecated. Please use `placementId` | Page ID | `123` | `Integer` | +| `impId` | No
Deprecated. Please use `placementId` | Imp ID | `1` | `Integer` | # Test Parameters @@ -24,15 +25,14 @@ var adUnits = [{ code: 'banner-1', mediaTypes: { banner: { - sizes: [[240, 400]], + sizes: [[240, 400], [300, 600]], } }, bids: [{ { bidder: 'yandex', params: { - pageId: 346580, - impId: 143, + placementId: '346580-1' }, } }] diff --git a/test/spec/modules/yandexBidAdapter_spec.js b/test/spec/modules/yandexBidAdapter_spec.js index eee53771a80..df91100b966 100644 --- a/test/spec/modules/yandexBidAdapter_spec.js +++ b/test/spec/modules/yandexBidAdapter_spec.js @@ -1,15 +1,14 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/yandexBidAdapter.js'; -import {parseUrl} from 'src/utils.js'; -import {BANNER} from '../../../src/mediaTypes'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/yandexBidAdapter.js'; +import { parseUrl } from 'src/utils.js'; +import { BANNER } from '../../../src/mediaTypes'; describe('Yandex adapter', function () { function getBidConfig() { return { bidder: 'yandex', params: { - pageId: 123, - impId: 1, + placementId: '123-1', }, }; } @@ -32,7 +31,7 @@ describe('Yandex adapter', function () { describe('isBidRequestValid', function () { it('should return true when required params found', function () { - const bid = getBidConfig(); + const bid = getBidRequest(); assert(spec.isBidRequestValid(bid)); }); @@ -40,18 +39,28 @@ describe('Yandex adapter', function () { expect(spec.isBidRequestValid({})).to.be.false; }); - it('should return false when required params.pageId are not passed', function () { + it('should return false when required params.placementId are not passed', function () { + const bid = getBidConfig(); + delete bid.params.placementId; + + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when required params.placementId are not valid', function () { const bid = getBidConfig(); - delete bid.params.pageId; + bid.params.placementId = '123'; - expect(spec.isBidRequestValid(bid)).to.be.false + expect(spec.isBidRequestValid(bid)).to.be.false; }); - it('should return false when required params.impId are not passed', function () { + it('should return true when passed deprecated placement config', function () { const bid = getBidConfig(); - delete bid.params.impId; + delete bid.params.placementId; + + bid.params.pageId = 123; + bid.params.impId = 1; - expect(spec.isBidRequestValid(bid)).to.be.false + expect(spec.isBidRequestValid(bid)); }); }); From 78aa883107e519980a87e298d0c8d8b6bac46029 Mon Sep 17 00:00:00 2001 From: Yoko OYAMA Date: Tue, 29 Nov 2022 02:41:19 +0900 Subject: [PATCH 481/569] Fluct Bid Adapter: add schain support (#9266) * add schain to req * run circleci Co-authored-by: Chris Huie --- modules/fluctBidAdapter.js | 5 +++ test/spec/modules/fluctBidAdapter_spec.js | 38 ++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js index ea634027dbe..004d44e6737 100644 --- a/modules/fluctBidAdapter.js +++ b/modules/fluctBidAdapter.js @@ -62,6 +62,11 @@ export const spec = { }); data.params = request.params; + + if (request.schain) { + data.schain = request.schain; + } + const searchParams = new URLSearchParams({ dfpUnitCode: request.params.dfpUnitCode, tagId: request.params.tagId, diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js index 8ef99727ce7..92a4b42f6f8 100644 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ b/test/spec/modules/fluctBidAdapter_spec.js @@ -94,7 +94,12 @@ describe('fluctAdapter', function () { expect(request.data.params.kv).to.eql(undefined); }); - it('includes filtered user.eids if any exists', function () { + it('includes no data.schain by default', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data.schain).to.eql(undefined); + }); + + it('includes filtered user.eids if any exist', function () { const bidRequests2 = bidRequests.map( (bidReq) => Object.assign(bidReq, { userIdAsEids: [ @@ -175,6 +180,37 @@ describe('fluctAdapter', function () { imsids: ['imsid1', 'imsid2'] }); }); + + it('includes data.schain if any exists', function () { + // this should be done by schain.js + const bidRequests2 = bidRequests.map( + (bidReq) => Object.assign(bidReq, { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: 'publisher-id', + hp: 1 + } + ] + } + }) + ); + const request = spec.buildRequests(bidRequests2, bidderRequest)[0]; + expect(request.data.schain).to.eql({ + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: 'publisher-id', + hp: 1 + } + ] + }); + }); }); describe('interpretResponse', function() { From 3261b061678ef9f565107de96b8457b35d763cd2 Mon Sep 17 00:00:00 2001 From: ssorleti <100858633+ssorleti@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:44:55 +0100 Subject: [PATCH 482/569] Update bucksense adapter - new server endpoint (#9292) --- modules/bucksenseBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bucksenseBidAdapter.js b/modules/bucksenseBidAdapter.js index fcf99179993..7b6c3911ea1 100644 --- a/modules/bucksenseBidAdapter.js +++ b/modules/bucksenseBidAdapter.js @@ -4,7 +4,7 @@ import { BANNER } from '../src/mediaTypes.js'; const WHO = 'BKSHBID-005'; const BIDDER_CODE = 'bucksense'; -const URL = 'https://prebid.bksn.se/prebidjs/'; +const URL = 'https://directo.prebidserving.com/prebidjs/'; export const spec = { code: BIDDER_CODE, From b50716816925f8c4865a21ad3969eaad20942a35 Mon Sep 17 00:00:00 2001 From: Yohan Boutin Date: Mon, 28 Nov 2022 18:45:35 +0100 Subject: [PATCH 483/569] Seedtag Bid Adapter : add support for inBanner and inStream (#9230) * use inBanner and inStream for video * remove duplicate video params, now use only params from adunit level * lint * improve unit test * fix adapter for instream support, and fix unit test * use inStream placement for instream context * use ALLOWED_DISPLAY_PLACEMENTS * fix lint error * empty commit to relaunch CI --- modules/seedtagBidAdapter.js | 123 ++++++++------- modules/seedtagBidAdapter.md | 60 ++++++++ test/spec/modules/seedtagBidAdapter_spec.js | 157 ++++++++------------ 3 files changed, 192 insertions(+), 148 deletions(-) diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index 850ac5610f5..1a4f903789b 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -1,19 +1,18 @@ import { isArray, _map, triggerPixel } from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js' -import { VIDEO, BANNER } from '../src/mediaTypes.js' +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'seedtag'; const SEEDTAG_ALIAS = 'st'; const SEEDTAG_SSP_ENDPOINT = 'https://s.seedtag.com/c/hb/bid'; const SEEDTAG_SSP_ONTIMEOUT_ENDPOINT = 'https://s.seedtag.com/se/hb/timeout'; -const ALLOWED_PLACEMENTS = { - inImage: true, - inScreen: true, - inArticle: true, - banner: true, - video: true -} +const ALLOWED_DISPLAY_PLACEMENTS = [ + 'inScreen', + 'inImage', + 'inArticle', + 'inBanner', +]; // Global Vendor List Id // https://iabeurope.eu/vendor-list-tcf-v2-0/ @@ -21,27 +20,33 @@ const GVLID = 157; const mediaTypesMap = { [BANNER]: 'display', - [VIDEO]: 'video' + [VIDEO]: 'video', }; const deviceConnection = { FIXED: 'fixed', MOBILE: 'mobile', - UNKNOWN: 'unknown' + UNKNOWN: 'unknown', }; const getConnectionType = () => { - const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {} + const connection = + navigator.connection || + navigator.mozConnection || + navigator.webkitConnection || + {}; switch (connection.type || connection.effectiveType) { case 'wifi': case 'ethernet': - return deviceConnection.FIXED + return deviceConnection.FIXED; case 'cellular': case 'wimax': - return deviceConnection.MOBILE + return deviceConnection.MOBILE; default: - const isMobile = /iPad|iPhone|iPod/.test(navigator.userAgent) || /android/i.test(navigator.userAgent) - return isMobile ? deviceConnection.UNKNOWN : deviceConnection.FIXED + const isMobile = + /iPad|iPhone|iPod/.test(navigator.userAgent) || + /android/i.test(navigator.userAgent); + return isMobile ? deviceConnection.UNKNOWN : deviceConnection.FIXED; } }; @@ -52,24 +57,32 @@ function mapMediaType(seedtagMediaType) { } function hasVideoMediaType(bid) { - return (!!bid.mediaTypes && !!bid.mediaTypes.video) || (!!bid.params && !!bid.params.video) + return !!bid.mediaTypes && !!bid.mediaTypes.video; } -function hasMandatoryParams(params) { +function hasMandatoryDisplayParams(bid) { + const p = bid.params; return ( - !!params.publisherId && - !!params.adUnitId && - !!params.placement && - !!ALLOWED_PLACEMENTS[params.placement] + !!p.publisherId && + !!p.adUnitId && + ALLOWED_DISPLAY_PLACEMENTS.indexOf(p.placement) > -1 ); } function hasMandatoryVideoParams(bid) { - const videoParams = getVideoParams(bid) + const videoParams = getVideoParams(bid); - return hasVideoMediaType(bid) && !!videoParams.playerSize && + return ( + !!bid.params.publisherId && + !!bid.params.adUnitId && + hasVideoMediaType(bid) && + !!videoParams.playerSize && isArray(videoParams.playerSize) && - videoParams.playerSize.length > 0; + videoParams.playerSize.length > 0 && + // only instream is supported for video + videoParams.context === 'instream' && + bid.params.placement === 'inStream' + ); } function buildBidRequest(validBidRequest) { @@ -89,15 +102,11 @@ function buildBidRequest(validBidRequest) { adUnitId: params.adUnitId, adUnitCode: validBidRequest.adUnitCode, placement: params.placement, - requestCount: validBidRequest.bidderRequestsCount || 1 // FIXME : in unit test the parameter bidderRequestsCount is undefined + requestCount: validBidRequest.bidderRequestsCount || 1, // FIXME : in unit test the parameter bidderRequestsCount is undefined }; - if (params.adPosition) { - bidRequest.adPosition = params.adPosition; - } - if (hasVideoMediaType(validBidRequest)) { - bidRequest.videoParams = getVideoParams(validBidRequest) + bidRequest.videoParams = getVideoParams(validBidRequest); } return bidRequest; @@ -113,13 +122,7 @@ function getVideoParams(validBidRequest) { videoParams.h = videoParams.playerSize[0][1]; } - const bidderVideoParams = (validBidRequest.params && validBidRequest.params.video) || {} - // override video params from seedtag bidder params - Object.keys(bidderVideoParams).forEach(key => { - videoParams[key] = validBidRequest.params.video[key] - }) - - return videoParams + return videoParams; } function buildBidResponse(seedtagBid) { @@ -136,8 +139,11 @@ function buildBidResponse(seedtagBid) { ttl: seedtagBid.ttl, nurl: seedtagBid.nurl, meta: { - advertiserDomains: seedtagBid && seedtagBid.adomain && seedtagBid.adomain.length > 0 ? seedtagBid.adomain : [] - } + advertiserDomains: + seedtagBid && seedtagBid.adomain && seedtagBid.adomain.length > 0 + ? seedtagBid.adomain + : [], + }, }; if (mediaType === VIDEO) { @@ -181,16 +187,21 @@ function ttfb() { export function getTimeoutUrl(data) { let queryParams = ''; if ( - isArray(data) && data[0] && - isArray(data[0].params) && data[0].params[0] + isArray(data) && + data[0] && + isArray(data[0].params) && + data[0].params[0] ) { const params = data[0].params[0]; - const timeout = data[0].timeout + const timeout = data[0].timeout; queryParams = - '?publisherToken=' + params.publisherId + - '&adUnitId=' + params.adUnitId + - '&timeout=' + timeout; + '?publisherToken=' + + params.publisherId + + '&adUnitId=' + + params.adUnitId + + '&timeout=' + + timeout; } return SEEDTAG_SSP_ONTIMEOUT_ENDPOINT + queryParams; } @@ -208,8 +219,8 @@ export const spec = { */ isBidRequestValid(bid) { return hasVideoMediaType(bid) - ? hasMandatoryParams(bid.params) && hasMandatoryVideoParams(bid) - : hasMandatoryParams(bid.params); + ? hasMandatoryVideoParams(bid) + : hasMandatoryDisplayParams(bid); }, /** @@ -237,24 +248,24 @@ export const spec = { payload['cd'] = bidderRequest.gdprConsent.consentString; } if (bidderRequest.uspConsent) { - payload['uspConsent'] = bidderRequest.uspConsent + payload['uspConsent'] = bidderRequest.uspConsent; } if (validBidRequests[0].schain) { payload.schain = validBidRequests[0].schain; } - let coppa = config.getConfig('coppa') + let coppa = config.getConfig('coppa'); if (coppa) { - payload.coppa = coppa + payload.coppa = coppa; } - const payloadString = JSON.stringify(payload) + const payloadString = JSON.stringify(payload); return { method: 'POST', url: SEEDTAG_SSP_ENDPOINT, - data: payloadString - } + data: payloadString, + }; }, /** @@ -308,6 +319,6 @@ export const spec = { if (bid && bid.nurl) { triggerPixel(bid.nurl); } - } -} + }, +}; registerBidder(spec); diff --git a/modules/seedtagBidAdapter.md b/modules/seedtagBidAdapter.md index 061a254c5fa..8ccfcfb701e 100644 --- a/modules/seedtagBidAdapter.md +++ b/modules/seedtagBidAdapter.md @@ -59,3 +59,63 @@ const adUnits = [ } ] ``` + +## InBanner +```js +const adUnits = [ + { + code: '/21804003197/prebid_test_300x250', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'seedtag', + params: { + publisherId: '0000-0000-01', // required + adUnitId: '0000', // required + placement: 'inBanner', // required + } + } + ] + } +] +``` + +## inStream Video +```js +var adUnits = [{ + code: 'video', + mediaTypes: { + video: { + context: 'instream', // required + playerSize: [640, 360], // required + // Video object as specified in OpenRTB 2.5 + mimes: ['video/mp4'], // recommended + minduration: 5, // optional + maxduration: 60, // optional + boxingallowed: 1, // optional + skip: 1, // optional + startdelay: 1, // optional + linearity: 1, // optional + battr: [1, 2], // optional + maxbitrate: 10, // optional + playbackmethod: [1], // optional + delivery: [1], // optional + placement: 1, // optional + } + }, + bids: [ + { + bidder: 'seedtag', + params: { + publisherId: '0000-0000-01', // required + adUnitId: '0000', // required + placement: 'inStream', // required + } + } + ] +}]; +``` diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index bd726bfe971..d0efd5f1f75 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -25,11 +25,11 @@ function getSlotConfigs(mediaTypes, params) { }; } -function createVideoSlotConfig(mediaType) { +function createInStreamSlotConfig(mediaType) { return getSlotConfigs(mediaType, { publisherId: PUBLISHER_ID, adUnitId: ADUNIT_ID, - placement: 'video', + placement: 'inStream', }); } @@ -48,13 +48,7 @@ describe('Seedtag Adapter', function () { } ); }; - const placements = [ - 'banner', - 'video', - 'inImage', - 'inScreen', - 'inArticle', - ]; + const placements = ['inBanner', 'inImage', 'inScreen', 'inArticle']; placements.forEach((placement) => { it('should be ' + placement, function () { const isBidRequestValid = spec.isBidRequestValid( @@ -67,39 +61,41 @@ describe('Seedtag Adapter', function () { }); describe('when video slot has all mandatory params', function () { it('should return true, when video context is instream', function () { - const slotConfig = getSlotConfigs( - { - video: { - context: 'instream', - playerSize: [[600, 200]], - }, + const slotConfig = createInStreamSlotConfig({ + video: { + context: 'instream', + playerSize: [[600, 200]], }, - { - publisherId: PUBLISHER_ID, - adUnitId: ADUNIT_ID, - placement: 'video', - } - ); + }); const isBidRequestValid = spec.isBidRequestValid(slotConfig); expect(isBidRequestValid).to.equal(true); }); - - it('should return true, when video context is outstream', function () { + it('should return true, when video context is instream, but placement is not inStream', function () { const slotConfig = getSlotConfigs( { video: { - context: 'outstream', + context: 'instream', playerSize: [[600, 200]], }, }, { publisherId: PUBLISHER_ID, adUnitId: ADUNIT_ID, - placement: 'video', + placement: 'inBanner', } ); const isBidRequestValid = spec.isBidRequestValid(slotConfig); - expect(isBidRequestValid).to.equal(true); + expect(isBidRequestValid).to.equal(false); + }); + it('should return false, when video context is outstream', function () { + const slotConfig = createInStreamSlotConfig({ + video: { + context: 'outstream', + playerSize: [[600, 200]], + }, + }); + const isBidRequestValid = spec.isBidRequestValid(slotConfig); + expect(isBidRequestValid).to.equal(false); }); }); }); @@ -112,7 +108,7 @@ describe('Seedtag Adapter', function () { const isBidRequestValid = spec.isBidRequestValid( createSlotConfig({ adUnitId: ADUNIT_ID, - placement: 'banner', + placement: 'inBanner', }) ); expect(isBidRequestValid).to.equal(false); @@ -121,7 +117,7 @@ describe('Seedtag Adapter', function () { const isBidRequestValid = spec.isBidRequestValid( createSlotConfig({ publisherId: PUBLISHER_ID, - placement: 'banner', + placement: 'inBanner', }) ); expect(isBidRequestValid).to.equal(false); @@ -146,47 +142,41 @@ describe('Seedtag Adapter', function () { expect(isBidRequestValid).to.equal(false); }); }); + describe('when video mediaType object is not correct', function () { - function createVideoSlotconfig(mediaType) { - return getSlotConfigs(mediaType, { - publisherId: PUBLISHER_ID, - adUnitId: ADUNIT_ID, - placement: 'video', - }); - } it('is a void object', function () { const isBidRequestValid = spec.isBidRequestValid( - createVideoSlotConfig({ video: {} }) + createInStreamSlotConfig({ video: {} }) ); expect(isBidRequestValid).to.equal(false); }); it('does not have playerSize.', function () { const isBidRequestValid = spec.isBidRequestValid( - createVideoSlotConfig({ video: { context: 'instream' } }) + createInStreamSlotConfig({ video: { context: 'instream' } }) ); expect(isBidRequestValid).to.equal(false); }); it('is outstream ', function () { const isBidRequestValid = spec.isBidRequestValid( - createVideoSlotConfig({ + createInStreamSlotConfig({ video: { context: 'outstream', playerSize: [[600, 200]], }, }) ); - expect(isBidRequestValid).to.equal(true); + expect(isBidRequestValid).to.equal(false); }); describe('order does not matter', function () { it('when video is not the first slot', function () { const isBidRequestValid = spec.isBidRequestValid( - createVideoSlotConfig({ banner: {}, video: {} }) + createInStreamSlotConfig({ banner: {}, video: {} }) ); expect(isBidRequestValid).to.equal(false); }); it('when video is the first slot', function () { const isBidRequestValid = spec.isBidRequestValid( - createVideoSlotConfig({ video: {}, banner: {} }) + createInStreamSlotConfig({ video: {}, banner: {} }) ); expect(isBidRequestValid).to.equal(false); }); @@ -200,21 +190,27 @@ describe('Seedtag Adapter', function () { refererInfo: { page: 'referer' }, timeout: 1000, }; - const mandatoryParams = { + const mandatoryDisplayParams = { publisherId: PUBLISHER_ID, adUnitId: ADUNIT_ID, - placement: 'banner', + placement: 'inBanner', + }; + const mandatoryVideoParams = { + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, + placement: 'inStream', }; - const inStreamParams = Object.assign({}, mandatoryParams, { - video: { - mimes: 'mp4', - }, - }); const validBidRequests = [ - getSlotConfigs({ banner: {} }, mandatoryParams), + getSlotConfigs({ banner: {} }, mandatoryDisplayParams), getSlotConfigs( - { video: { context: 'instream', playerSize: [[300, 200]] } }, - inStreamParams + { + video: { + context: 'instream', + playerSize: [[300, 200]], + mimes: ['video/mp4'], + }, + }, + mandatoryVideoParams ), ]; it('Url params should be correct ', function () { @@ -239,26 +235,6 @@ describe('Seedtag Adapter', function () { expect(data.bidRequests[0].adUnitCode).to.equal('adunit-code'); }); - describe('adPosition param', function () { - it('should sended when publisher set adPosition param', function () { - const params = Object.assign({}, mandatoryParams, { - adPosition: 1, - }); - const validBidRequests = [getSlotConfigs({ banner: {} }, params)]; - const request = spec.buildRequests(validBidRequests, bidderRequest); - const data = JSON.parse(request.data); - expect(data.bidRequests[0].adPosition).to.equal(1); - }); - it('should not sended when publisher has not set adPosition param', function () { - const validBidRequests = [ - getSlotConfigs({ banner: {} }, mandatoryParams), - ]; - const request = spec.buildRequests(validBidRequests, bidderRequest); - const data = JSON.parse(request.data); - expect(data.bidRequests[0].adPosition).to.equal(undefined); - }); - }); - describe('GDPR params', function () { describe('when there arent consent management platform', function () { it('cmp should be false', function () { @@ -310,10 +286,7 @@ describe('Seedtag Adapter', function () { bidderRequest['uspConsent'] = uspConsent; - const request = spec.buildRequests( - validBidRequests, - bidderRequest - ); + const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.uspConsent).to.not.exist; @@ -347,7 +320,7 @@ describe('Seedtag Adapter', function () { ); expect(videoBid.supplyTypes[0]).to.equal('video'); expect(videoBid.adUnitId).to.equal('000000'); - expect(videoBid.videoParams.mimes).to.equal('mp4'); + expect(videoBid.videoParams.mimes).to.eql(['video/mp4']); expect(videoBid.videoParams.w).to.equal(300); expect(videoBid.videoParams.h).to.equal(200); expect(videoBid.sizes[0][0]).to.equal(300); @@ -360,27 +333,27 @@ describe('Seedtag Adapter', function () { describe('COPPA param', function () { it('should add COPPA param to payload when prebid config has parameter COPPA equal to true', function () { - config.setConfig({ coppa: true }) + config.setConfig({ coppa: true }); const request = spec.buildRequests(validBidRequests, bidderRequest); const data = JSON.parse(request.data); expect(data.coppa).to.equal(true); - }) + }); it('should not add COPPA param to payload when prebid config has parameter COPPA equal to false', function () { - config.setConfig({ coppa: false }) + config.setConfig({ coppa: false }); const request = spec.buildRequests(validBidRequests, bidderRequest); const data = JSON.parse(request.data); expect(data.coppa).to.be.undefined; - }) + }); it('should not add COPPA param to payload when prebid config has not parameter COPPA', function () { const request = spec.buildRequests(validBidRequests, bidderRequest); const data = JSON.parse(request.data); expect(data.coppa).to.be.undefined; - }) - }) + }); + }); describe('schain param', function () { it('should add schain to payload when exposed on validBidRequest', function () { // https://github.com/prebid/Prebid.js/blob/master/modules/schain.md#sample-code-for-passing-the-schain-object @@ -560,11 +533,11 @@ describe('Seedtag Adapter', function () { const timeoutUrl = getTimeoutUrl(timeoutData); expect(timeoutUrl).to.equal( 'https://s.seedtag.com/se/hb/timeout?publisherToken=' + - params.publisherId + - '&adUnitId=' + - params.adUnitId + - '&timeout=' + - timeout + params.publisherId + + '&adUnitId=' + + params.adUnitId + + '&timeout=' + + timeout ); }); @@ -576,11 +549,11 @@ describe('Seedtag Adapter', function () { expect( utils.triggerPixel.calledWith( 'https://s.seedtag.com/se/hb/timeout?publisherToken=' + - params.publisherId + - '&adUnitId=' + - params.adUnitId + - '&timeout=' + - timeout + params.publisherId + + '&adUnitId=' + + params.adUnitId + + '&timeout=' + + timeout ) ).to.equal(true); }); From fe652b47deecf0eab3d16bfa02d1bbaa5b032c2b Mon Sep 17 00:00:00 2001 From: GeoEdge-r-and-d <72186958+GeoEdge-r-and-d@users.noreply.github.com> Date: Mon, 28 Nov 2022 20:22:03 +0200 Subject: [PATCH 484/569] Geoedge RTD module: support billing events (#9267) * Add billable events for applicable winning bids * Update test for billable events * Add meta bid advertiser domains collection * Revert "Add meta bid advertiser domains collection" This reverts commit 09c19c90b4dc9d234f282de8adb40850cce31101. * Add meta bid advertiser domains collection * Update geoedgeRtdProvider_spec.js Force circleci Co-authored-by: daniel manan Co-authored-by: Patrick McCann --- modules/geoedgeRtdProvider.js | 22 ++++++++++++++++++++ test/spec/modules/geoedgeRtdProvider_spec.js | 15 +++++++++++++ 2 files changed, 37 insertions(+) diff --git a/modules/geoedgeRtdProvider.js b/modules/geoedgeRtdProvider.js index 001ef67b66a..6f910632fbc 100644 --- a/modules/geoedgeRtdProvider.js +++ b/modules/geoedgeRtdProvider.js @@ -18,6 +18,8 @@ import { submodule } from '../src/hook.js'; import { ajax } from '../src/ajax.js'; import { generateUUID, insertElement, isEmpty, logError } from '../src/utils.js'; +import * as events from '../src/events.js'; +import CONSTANTS from '../src/constants.json'; /** @type {string} */ const SUBMODULE_NAME = 'geoedge'; @@ -105,6 +107,7 @@ function getMacros(bid, key) { '%%PATTERN:hb_bidder%%': bid.bidderCode, '%_isHb!': true, '%_hbcid!': bid.creativeId || '', + '%_hbadomains': bid.meta && bid.meta.advertiserDomains, '%%PATTERN:hb_pb%%': bid.pbHg, '%%SITE%%': location.hostname, '%_pimp%': PV_ID @@ -184,6 +187,24 @@ function conditionallyWrap(bidResponse, config, userConsent) { } } +/** + * Fire billable events for applicable bids + */ +function fireBillableEventsForApplicableBids(params) { + events.on(CONSTANTS.EVENTS.BID_WON, function (winningBid) { + if (shouldWrap(winningBid, params)) { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: SUBMODULE_NAME, + billingId: generateUUID(), + type: 'impression', + transactionId: winningBid.transactionId, + auctionId: winningBid.auctionId, + bidId: winningBid.requestId + }); + } + }); +} + function init(config, userConsent) { let params = config.params; if (!params || !params.key) { @@ -191,6 +212,7 @@ function init(config, userConsent) { return false; } preloadClient(params.key); + fireBillableEventsForApplicableBids(params); return true; } diff --git a/test/spec/modules/geoedgeRtdProvider_spec.js b/test/spec/modules/geoedgeRtdProvider_spec.js index cf4e0b53fde..eec1feff87a 100644 --- a/test/spec/modules/geoedgeRtdProvider_spec.js +++ b/test/spec/modules/geoedgeRtdProvider_spec.js @@ -2,6 +2,8 @@ import * as utils from '../../../src/utils.js'; import * as hook from '../../../src/hook.js' import { beforeInit, geoedgeSubmodule, setWrapper, wrapper, htmlPlaceholder, WRAPPER_URL, getClientUrl } from '../../../modules/geoedgeRtdProvider.js'; import { server } from '../../../test/mocks/xhr.js'; +import * as events from '../../../src/events.js'; +import CONSTANTS from '../../../src/constants.json'; let key = '123123123'; function makeConfig() { @@ -88,6 +90,19 @@ describe('Geoedge RTD module', function () { let isLinkPreloadAsScript = arg => arg.tagName === 'LINK' && arg.rel === 'preload' && arg.as === 'script' && arg.href === getClientUrl(key); expect(insertElementStub.calledWith(sinon.match(isLinkPreloadAsScript))).to.equal(true); }); + it('should emit billable events with applicable winning bids', function () { + let applicableBid = mockBid('bidderA'); + let nonApplicableBid = mockBid('bidderB'); + let counter = 0; + events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, function (event) { + if (event.vendor === 'geoedge' && event.type === 'impression') { + counter += 1; + } + }); + events.emit(CONSTANTS.EVENTS.BID_WON, applicableBid); + events.emit(CONSTANTS.EVENTS.BID_WON, nonApplicableBid); + expect(counter).to.equal(1); + }); }); describe('onBidResponseEvent', function () { let bidFromA = mockBid('bidderA'); From ad595cc7e54b81ebbece381c9df9bb36d526ddb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Tue, 29 Nov 2022 16:43:26 +0200 Subject: [PATCH 485/569] Vidazoo Bid Adapter: added bid request params (gpid, cat, pagecat) (#9293) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * added bid request params * making the linter happy :) Co-authored-by: roman Co-authored-by: Saar Amrani <89377180+saar120@users.noreply.github.com> Co-authored-by: Saar Amrani --- modules/vidazooBidAdapter.js | 9 ++- test/spec/modules/vidazooBidAdapter_spec.js | 62 +++++++++++++-------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index ac74dd18405..39c407ae8a6 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -70,6 +70,10 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const subDomain = extractSubDomain(params); const ptrace = getCacheOpt(); + const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); + const cat = deepAccess(bidderRequest, 'ortb2.site.cat', []); + const pagecat = deepAccess(bidderRequest, 'ortb2.site.pagecat', []); + if (isFn(bid.getFloor)) { const floorInfo = bid.getFloor({ currency: 'USD', @@ -99,7 +103,10 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { prebidVersion: '$prebid.version$', res: `${screen.width}x${screen.height}`, schain: schain, - ptrace: ptrace + ptrace: ptrace, + gpid: gpid, + cat: cat, + pagecat: pagecat }; appendUserIdsToRequestPayload(data, userId); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 856ceaf438d..7c7f27a830b 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, SUPPORTED_ID_SYSTEMS, @@ -15,8 +15,8 @@ import { getVidazooSessionId, } from 'modules/vidazooBidAdapter.js'; import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; const SUB_DOMAIN = 'openrtb'; @@ -38,7 +38,12 @@ const BID = { 'sizes': [[300, 250], [300, 600]], 'bidderRequestId': '1fdb5ff1b6eaa7', 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', - 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc' + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'ortb2Imp': { + 'ext': { + 'gpid': '1234567890' + } + } }; const BIDDER_REQUEST = { @@ -50,7 +55,13 @@ const BIDDER_REQUEST = { 'refererInfo': { 'page': 'https://www.greatsite.com', 'ref': 'https://www.somereferrer.com' - } + }, + 'ortb2': { + 'site': { + 'cat': ['IAB2'], + 'pagecat': ['IAB2-2'] + } + }, }; const SERVER_RESPONSE = { @@ -85,7 +96,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -150,7 +161,7 @@ describe('VidazooBidAdapter', function () { before(function () { $$PREBID_GLOBAL$$.bidderSettings = { vidazoo: { - storageAllowed: true + storageAllowed: true, } }; sandbox = sinon.sandbox.create(); @@ -187,6 +198,9 @@ describe('VidazooBidAdapter', function () { uqs: getTopWindowQueryParams(), 'ext.param1': 'loremipsum', 'ext.param2': 'dolorsitamet', + gpid: '1234567890', + cat: ['IAB2'], + pagecat: ['IAB2-2'] } }); }); @@ -198,7 +212,7 @@ describe('VidazooBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -207,7 +221,7 @@ describe('VidazooBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.cootlogix.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' @@ -215,7 +229,7 @@ describe('VidazooBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.cootlogix.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', @@ -231,12 +245,12 @@ describe('VidazooBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -276,11 +290,11 @@ describe('VidazooBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'parrableId': - return {eid: id}; + return { eid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -299,18 +313,18 @@ describe('VidazooBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -423,7 +437,7 @@ describe('VidazooBidAdapter', function () { now }); setStorageItem('myKey', 2020); - const {value, created} = getStorageItem('myKey'); + const { value, created } = getStorageItem('myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -439,8 +453,8 @@ describe('VidazooBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); From 64e557abfa36f9387678e7ddeb784091cb4419a7 Mon Sep 17 00:00:00 2001 From: JulieLorin Date: Tue, 29 Nov 2022 16:32:01 +0100 Subject: [PATCH 486/569] PBjs Core : send native targetings for ortb response (#9252) * PBjs Core : send native targetings for ortb response * Add legacy native properties regardless of response mediaType * add a test for addLegacyFieldsIfNeeded * fix lint for test Co-authored-by: Demetrio Girardi --- src/auction.js | 20 ++++- src/native.js | 51 ++++++++----- test/spec/auctionmanager_spec.js | 76 +++++++++++++++++++ test/spec/native_spec.js | 124 +++++++++++++++++-------------- 4 files changed, 193 insertions(+), 78 deletions(-) diff --git a/src/auction.js b/src/auction.js index 6376457c1d6..563eb85e788 100644 --- a/src/auction.js +++ b/src/auction.js @@ -76,7 +76,7 @@ import { timestamp } from './utils.js'; import {getPriceBucketString} from './cpmBucketManager.js'; -import {getNativeTargeting} from './native.js'; +import {getNativeTargeting, toLegacyResponse} from './native.js'; import {getCacheUrl, store} from './videoCache.js'; import {Renderer} from './Renderer.js'; import {config} from './config.js'; @@ -462,9 +462,14 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM handleBidResponse(adUnitCode, bid, (done) => { let bidResponse = getPreparedBidForAuction(bid); - if (bidResponse.mediaType === 'video') { + if (bidResponse.mediaType === VIDEO) { tryAddVideoBid(auctionInstance, bidResponse, done); } else { + if (FEATURES.NATIVE && bidResponse.native != null && typeof bidResponse.native === 'object') { + // NOTE: augment bidResponse.native even if bidResponse.mediaType !== NATIVE; it's possible + // to treat banner responses as native + addLegacyFieldsIfNeeded(bidResponse); + } addBidToAuction(auctionInstance, bidResponse); done(); } @@ -593,6 +598,17 @@ function tryAddVideoBid(auctionInstance, bidResponse, afterBidAdded, {index = au } } +// Native bid response might be in ortb2 format - adds legacy field for backward compatibility +const addLegacyFieldsIfNeeded = (bidResponse) => { + const nativeOrtbRequest = auctionManager.index.getAdUnit(bidResponse)?.nativeOrtbRequest; + const nativeOrtbResponse = bidResponse.native?.ortb + + if (nativeOrtbRequest && nativeOrtbResponse) { + const legacyResponse = toLegacyResponse(nativeOrtbResponse, nativeOrtbRequest); + Object.assign(bidResponse.native, legacyResponse); + } +} + const storeInCache = (batch) => { store(batch.map(entry => entry.bidResponse), function (error, cacheIds) { cacheIds.forEach((cacheId, i) => { diff --git a/src/native.js b/src/native.js index 022ece457f5..25f8c38cb30 100644 --- a/src/native.js +++ b/src/native.js @@ -23,7 +23,7 @@ export const NATIVE_TARGETING_KEYS = Object.keys(CONSTANTS.NATIVE_KEYS).map( key => CONSTANTS.NATIVE_KEYS[key] ); -const IMAGE = { +export const IMAGE = { ortb: { ver: '1.2', assets: [ @@ -385,26 +385,14 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) { return keyValues; } -const getNativeRequest = (bidResponse) => auctionManager.index.getAdUnit(bidResponse)?.nativeOrtbRequest; - -function assetsMessage(data, adObject, keys, {getNativeReq = getNativeRequest} = {}) { +function assetsMessage(data, adObject, keys) { const message = { message: 'assetResponse', adId: data.adId, }; - // Pass to Prebid Universal Creative all assets, the legacy ones + the ortb ones (under ortb property) - const ortbRequest = getNativeReq(adObject); let nativeResp = adObject.native; - const ortbResponse = adObject.native?.ortb; - let legacyResponse = {}; - if (ortbRequest && ortbResponse) { - legacyResponse = toLegacyResponse(ortbResponse, ortbRequest); - nativeResp = { - ...adObject.native, - ...legacyResponse - }; - } + if (adObject.native.ortb) { message.ortb = adObject.native.ortb; } @@ -435,13 +423,13 @@ function assetsMessage(data, adObject, keys, {getNativeReq = getNativeRequest} = * Constructs a message object containing asset values for each of the * requested data keys. */ -export function getAssetMessage(data, adObject, {getNativeReq = getNativeRequest} = {}) { +export function getAssetMessage(data, adObject) { const keys = data.assets.map((k) => getKeyByValue(CONSTANTS.NATIVE_KEYS, k)); - return assetsMessage(data, adObject, keys, {getNativeReq}); + return assetsMessage(data, adObject, keys); } -export function getAllAssetsMessage(data, adObject, {getNativeReq = getNativeRequest} = {}) { - return assetsMessage(data, adObject, null, {getNativeReq}); +export function getAllAssetsMessage(data, adObject) { + return assetsMessage(data, adObject, null); } /** @@ -749,7 +737,7 @@ export function toOrtbNativeResponse(legacyResponse, ortbRequest) { * @param {*} ortbRequest the ortb request, useful to match ids. * @returns an object containing the response in legacy native format: { title: "this is a title", image: ... } */ -function toLegacyResponse(ortbResponse, ortbRequest) { +export function toLegacyResponse(ortbResponse, ortbRequest) { const legacyResponse = {}; const requestAssets = ortbRequest?.assets || []; legacyResponse.clickUrl = ortbResponse.link.url; @@ -764,6 +752,29 @@ function toLegacyResponse(ortbResponse, ortbRequest) { legacyResponse[PREBID_NATIVE_DATA_KEYS_TO_ORTB_INVERSE[NATIVE_ASSET_TYPES_INVERSE[requestAsset.data.type]]] = asset.data.value; } } + + // Handle trackers + legacyResponse.impressionTrackers = []; + let jsTrackers = []; + + if (ortbRequest?.imptrackers) { + legacyResponse.impressionTrackers.push(...ortbRequest.imptrackers); + } + for (const eventTracker of ortbResponse?.eventtrackers || []) { + if (eventTracker.event === TRACKER_EVENTS.impression && eventTracker.method === TRACKER_METHODS.img) { + legacyResponse.impressionTrackers.push(eventTracker.url); + } + if (eventTracker.event === TRACKER_EVENTS.impression && eventTracker.method === TRACKER_METHODS.js) { + jsTrackers.push(eventTracker.url); + } + } + + jsTrackers = jsTrackers.map(url => ``); + if (ortbResponse?.jstracker) { jsTrackers.push(ortbResponse.jstracker); } + if (jsTrackers.length) { + legacyResponse.javascriptTrackers = jsTrackers.join('\n'); + } + return legacyResponse; } diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 443147f5d20..46350895050 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -22,6 +22,7 @@ import 'modules/debugging/index.js' // some tests look for debugging side effect import {AuctionIndex} from '../../src/auctionIndex.js'; import {expect} from 'chai'; import {deepClone} from '../../src/utils.js'; +import { IMAGE as ortbNativeRequest } from 'src/native.js'; var assert = require('assert'); @@ -1276,6 +1277,81 @@ describe('auctionmanager.js', function () { }); }); + if (FEATURES.NATIVE) { + describe('addBidResponse native', function () { + let makeRequestsStub; + let ajaxStub; + let spec; + let auction; + + beforeEach(function () { + makeRequestsStub = sinon.stub(adapterManager, 'makeBidRequests'); + ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(mockAjaxBuilder); + + const adUnits = [{ + code: ADUNIT_CODE, + transactionId: ADUNIT_CODE, + bids: [ + {bidder: BIDDER_CODE, params: {placementId: 'id'}}, + ], + nativeOrtbRequest: ortbNativeRequest.ortb, + mediaTypes: { native: { type: 'image' } } + }]; + auction = auctionModule.newAuction({adUnits, adUnitCodes: [ADUNIT_CODE], callback: function() {}, cbTimeout: 3000}); + indexAuctions = [auction]; + const createAuctionStub = sinon.stub(auctionModule, 'newAuction'); + createAuctionStub.returns(auction); + + spec = mockBidder(BIDDER_CODE); + registerBidder(spec); + }); + + afterEach(function () { + ajaxStub.restore(); + auctionModule.newAuction.restore(); + adapterManager.makeBidRequests.restore(); + }); + + it('should add legacy fields to native response', function () { + let nativeBid = mockBid(); + nativeBid.mediaType = 'native'; + nativeBid.native = { + ortb: { + ver: '1.2', + assets: [ + { id: 2, title: { text: 'Sample title' } }, + { id: 4, data: { value: 'Sample body' } }, + { id: 3, data: { value: 'Sample sponsoredBy' } }, + { id: 1, img: { url: 'https://www.example.com/image.png', w: 200, h: 200 } }, + { id: 5, img: { url: 'https://www.example.com/icon.png', w: 32, h: 32 } } + ], + link: { url: 'http://www.click.com' }, + eventtrackers: [ + { event: 1, method: 1, url: 'http://www.imptracker.com' }, + { event: 1, method: 2, url: 'http://www.jstracker.com/file.js' } + ] + } + } + + let bidRequest = mockBidRequest(nativeBid, { mediaType: { native: ortbNativeRequest } }); + makeRequestsStub.returns([bidRequest]); + + spec.interpretResponse.returns(nativeBid); + auction.callBids(); + + const addedBid = auction.getBidsReceived().pop(); + assert.equal(addedBid.native.body, 'Sample body') + assert.equal(addedBid.native.title, 'Sample title') + assert.equal(addedBid.native.sponsoredBy, 'Sample sponsoredBy') + assert.equal(addedBid.native.clickUrl, 'http://www.click.com') + assert.equal(addedBid.native.image, 'https://www.example.com/image.png') + assert.equal(addedBid.native.icon, 'https://www.example.com/icon.png') + assert.equal(addedBid.native.impressionTrackers[0], 'http://www.imptracker.com') + assert.equal(addedBid.native.javascriptTrackers, '') + }); + }); + } + describe('getMediaTypeGranularity', function () { it('video', function () { let mediaTypes = { video: {id: '1'} }; diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index 23350781a3d..2b7c2b88449 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -5,6 +5,7 @@ import { nativeBidIsValid, getAssetMessage, getAllAssetsMessage, + toLegacyResponse, decorateAdUnitsWithNativeParams, isOpenRTBBidRequestValid, isNativeOpenRTBBidValid, @@ -37,6 +38,7 @@ const bid = { clickTrackers: ['https://tracker.example'], impressionTrackers: ['https://impression.example'], javascriptTrackers: '', + privacyLink: 'https://privacy-link.example', ext: { foo: 'foo-value', baz: 'baz-value', @@ -98,9 +100,18 @@ const ortbBid = { privacy: 'https://privacy-link.example', ver: '1.2' } - }, + } }; +const completeNativeBid = { + adId: '123', + transactionId: 'au', + native: { + ...bid.native, + ...ortbBid.native + } +} + const ortbRequest = { assets: [ { @@ -224,6 +235,14 @@ describe('native.js', function () { expect(targeting.hb_native_baz).to.equal('hb_native_baz:123'); }); + it('sends placeholdes targetings with ortb native response', function () { + const targeting = getNativeTargeting(completeNativeBid); + + expect(targeting[CONSTANTS.NATIVE_KEYS.title]).to.equal('Native Creative'); + expect(targeting[CONSTANTS.NATIVE_KEYS.body]).to.equal('Cool description great stuff'); + expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal('https://www.link.example'); + }); + it('should only include native targeting keys with values', function () { const adUnit = { transactionId: 'au', @@ -302,6 +321,10 @@ describe('native.js', function () { required: false, sendTargetingKeys: false, }, + privacyLink: { + required: false, + sendTargetingKeys: false, + }, ext: { foo: { required: false, @@ -348,6 +371,7 @@ describe('native.js', function () { CONSTANTS.NATIVE_KEYS.icon, CONSTANTS.NATIVE_KEYS.sponsoredBy, CONSTANTS.NATIVE_KEYS.clickUrl, + CONSTANTS.NATIVE_KEYS.privacyLink, CONSTANTS.NATIVE_KEYS.rendererUrl, ]); @@ -380,6 +404,7 @@ describe('native.js', function () { CONSTANTS.NATIVE_KEYS.icon, CONSTANTS.NATIVE_KEYS.sponsoredBy, CONSTANTS.NATIVE_KEYS.clickUrl, + CONSTANTS.NATIVE_KEYS.privacyLink, ]); expect(bid.native.adTemplate).to.deep.equal( @@ -437,9 +462,9 @@ describe('native.js', function () { adId: '123', }; - const message = getAllAssetsMessage(messageRequest, bid, {getNativeReq: () => null}); + const message = getAllAssetsMessage(messageRequest, bid); - expect(message.assets.length).to.equal(9); + expect(message.assets.length).to.equal(10); expect(message.assets).to.deep.include({ key: 'body', value: bid.native.body, @@ -485,7 +510,7 @@ describe('native.js', function () { adId: '123', }; - const message = getAllAssetsMessage(messageRequest, bidWithUndefinedFields, {getNativeReq: () => null}); + const message = getAllAssetsMessage(messageRequest, bidWithUndefinedFields); expect(message.assets.length).to.equal(4); expect(message.assets).to.deep.include({ @@ -506,16 +531,16 @@ describe('native.js', function () { }); }); - it('creates native all asset message with OpenRTB format', function () { + it('creates native all asset message with complete format', function () { const messageRequest = { message: 'Prebid Native', action: 'allAssetRequest', adId: '123', }; - const message = getAllAssetsMessage(messageRequest, ortbBid, {getNativeReq: () => ortbRequest}); + const message = getAllAssetsMessage(messageRequest, completeNativeBid); - expect(message.assets.length).to.equal(8); + expect(message.assets.length).to.equal(10); expect(message.assets).to.deep.include({ key: 'body', value: bid.native.body, @@ -548,6 +573,14 @@ describe('native.js', function () { key: 'privacyLink', value: ortbBid.native.ortb.privacy, }); + expect(message.assets).to.deep.include({ + key: 'foo', + value: bid.native.ext.foo, + }); + expect(message.assets).to.deep.include({ + key: 'baz', + value: bid.native.ext.baz, + }); }); const SAMPLE_ORTB_REQUEST = toOrtbNativeRequest({ @@ -555,60 +588,39 @@ describe('native.js', function () { body: 'vbody' }); const SAMPLE_ORTB_RESPONSE = { - native: { - ortb: { - link: { - url: 'url' - }, - assets: [ - { - id: 0, - title: { - text: 'vtitle' - } - }, - { - id: 1, - data: { - value: 'vbody' - } - } - ] + link: { + url: 'url' + }, + assets: [ + { + id: 0, + title: { + text: 'vtitle' + } + }, + { + id: 1, + data: { + value: 'vbody' + } } - } + ], + eventtrackers: [ + { event: 1, method: 1, url: 'https://sampleurl.com' }, + { event: 1, method: 2, url: 'https://sampleurljs.com' } + ] } - describe('getAllAssetsMessage', () => { + describe('toLegacyResponse', () => { it('returns assets in legacy format for ortb responses', () => { - const actual = getAllAssetsMessage({}, SAMPLE_ORTB_RESPONSE, {getNativeReq: () => SAMPLE_ORTB_REQUEST}); - expect(actual.assets).to.eql([ - { - key: 'clickUrl', - value: 'url' - }, - { - key: 'title', - value: 'vtitle' - }, - { - key: 'body', - value: 'vbody' - }, - ]) + const actual = toLegacyResponse(SAMPLE_ORTB_RESPONSE, SAMPLE_ORTB_REQUEST); + expect(actual.body).to.equal('vbody'); + expect(actual.title).to.equal('vtitle'); + expect(actual.clickUrl).to.equal('url'); + expect(actual.javascriptTrackers).to.equal(''); + expect(actual.impressionTrackers.length).to.equal(1); + expect(actual.impressionTrackers[0]).to.equal('https://sampleurl.com'); }); }); - describe('getAssetsMessage', () => { - Object.entries({ - 'hb_native_title': {key: 'title', value: 'vtitle'}, - 'hb_native_body': {key: 'body', value: 'vbody'} - }).forEach(([tkey, assetVal]) => { - it(`returns ${tkey} asset in legacy format for ortb responses`, () => { - const actual = getAssetMessage({ - assets: [tkey] - }, SAMPLE_ORTB_RESPONSE, {getNativeReq: () => SAMPLE_ORTB_REQUEST}) - expect(actual.assets).to.eql([assetVal]) - }) - }) - }) }); describe('validate native openRTB', function () { From dd7d8a642598045b620c80236e6a568b3f14d9ba Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 29 Nov 2022 19:23:29 +0100 Subject: [PATCH 487/569] Impactify Bid Adapter: add support for BidFloor (#9277) * Add support of getFloor function * Add support of getFloor function * Add support of getFloor function * Add support of getFloor function * Add unit test for bid floor * Add unit test for bid floor Co-authored-by: Thomas De Stefano --- modules/impactifyBidAdapter.js | 20 ++++++++++++++++++- test/spec/modules/impactifyBidAdapter_spec.js | 13 ++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index 0717cf43741..04f34bdc7d9 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -1,7 +1,7 @@ import { deepAccess, deepSetValue, generateUUID } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import { createEidsArray } from './userId/eids.js'; const BIDDER_CODE = 'impactify'; @@ -27,6 +27,18 @@ const getDeviceType = () => { return 2; }; +const getFloor = (bid) => { + const floorInfo = bid.getFloor({ + currency: DEFAULT_CURRENCY, + mediaType: '*', + size: '*' + }); + if (typeof floorInfo === 'object' && floorInfo.currency === DEFAULT_CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { + return parseFloat(floorInfo.floor); + } + return null; +} + const createOpenRtbRequest = (validBidRequests, bidderRequest) => { // Create request and set imp bids inside let request = { @@ -114,6 +126,12 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => { if (bid.params.container) { imp.ext.impactify.container = bid.params.container; } + if (typeof bid.getFloor === 'function') { + const floor = getFloor(bid); + if (floor) { + imp.bidfloor = floor; + } + } request.imp.push(imp); }); diff --git a/test/spec/modules/impactifyBidAdapter_spec.js b/test/spec/modules/impactifyBidAdapter_spec.js index 8bb2d089ad8..215972ff450 100644 --- a/test/spec/modules/impactifyBidAdapter_spec.js +++ b/test/spec/modules/impactifyBidAdapter_spec.js @@ -166,6 +166,19 @@ describe('ImpactifyAdapter', function () { } }; + it('should pass bidfloor', function () { + videoBidRequests[0].getFloor = function() { + return { + currency: 'USD', + floor: 1.23, + } + } + + const res = spec.buildRequests(videoBidRequests, videoBidderRequest) + const resData = JSON.parse(res.data) + expect(resData.imp[0].bidfloor).to.equal(1.23) + }); + it('sends video bid request to ENDPOINT via POST', function () { const request = spec.buildRequests(videoBidRequests, videoBidderRequest); expect(request.url).to.equal(ORIGIN + AUCTIONURI); From 29c0d82898c6dbd171fe61814322d6188d91ef86 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 29 Nov 2022 14:18:57 -0500 Subject: [PATCH 488/569] TTD Bid Adapter: add support for regs.gpp (#9274) * Update ttdBidAdapter.js * Update ttdBidAdapter.js * Update ttdBidAdapter_spec.js * Update ttdBidAdapter_spec.js * Update ttdBidAdapter_spec.js * fix linting * Update ttdBidAdapter_spec.js * Update ttdBidAdapter_spec.js * Update ttdBidAdapter.js * Update ttdBidAdapter_spec.js * Update ttdBidAdapter.js * Update ttdBidAdapter.js * Update ttdBidAdapter.js * Update ttdBidAdapter.js * Update ttdBidAdapter.js * Update ttdBidAdapter.js * Update ttdBidAdapter.js Co-authored-by: Chris Huie --- modules/ttdBidAdapter.js | 4 ++++ test/spec/modules/ttdBidAdapter_spec.js | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/modules/ttdBidAdapter.js b/modules/ttdBidAdapter.js index 56dc44827b7..884c43c438a 100644 --- a/modules/ttdBidAdapter.js +++ b/modules/ttdBidAdapter.js @@ -38,6 +38,10 @@ function getRegs(bidderRequest) { if (config.getConfig('coppa') === true) { regs.coppa = 1; } + if (bidderRequest.ortb2?.regs) { + utils.mergeDeep(regs, bidderRequest.ortb2.regs); + } + return regs; } diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index 346f1ef88f6..f45872faec9 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -400,6 +400,20 @@ describe('ttdBidAdapter', function () { expect(requestBody.regs.coppa).to.equal(1); }); + it('adds gpp consent info to the request', function () { + const ortb2 = { + regs: { + gpp: 'somegppstring', + gpp_sid: [6, 7] + } + }; + let clonedBidderRequest = {...deepClone(baseBidderRequest), ortb2}; + const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; + config.resetConfig(); + expect(requestBody.regs.gpp).to.equal('somegppstring'); + expect(requestBody.regs.gpp_sid).to.eql([6, 7]); + }); + it('adds schain info to the request', function () { const schain = { 'ver': '1.0', From fa3b66548b91187c837f7112c87bcbcf29e15917 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 29 Nov 2022 15:58:40 -0500 Subject: [PATCH 489/569] Revert "fix for broken download bundle https://github.com/prebid/prebid.github.io/issues/4177 (#9289)" (#9298) This reverts commit 5a9aaa8ca5bb47393ddfb08b00d1c4e0af5fd850. --- modules/{kueezrtbBidAdapter.js => kueezRtbBidAdapter.js} | 0 modules/{kueezrtbBidAdapter.md => kueezRtbBidAdapter.md} | 0 .../{kueezrtbBidAdapter_spec.js => kueezRtbBidAdapter_spec.js} | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename modules/{kueezrtbBidAdapter.js => kueezRtbBidAdapter.js} (100%) rename modules/{kueezrtbBidAdapter.md => kueezRtbBidAdapter.md} (100%) rename test/spec/modules/{kueezrtbBidAdapter_spec.js => kueezRtbBidAdapter_spec.js} (99%) diff --git a/modules/kueezrtbBidAdapter.js b/modules/kueezRtbBidAdapter.js similarity index 100% rename from modules/kueezrtbBidAdapter.js rename to modules/kueezRtbBidAdapter.js diff --git a/modules/kueezrtbBidAdapter.md b/modules/kueezRtbBidAdapter.md similarity index 100% rename from modules/kueezrtbBidAdapter.md rename to modules/kueezRtbBidAdapter.md diff --git a/test/spec/modules/kueezrtbBidAdapter_spec.js b/test/spec/modules/kueezRtbBidAdapter_spec.js similarity index 99% rename from test/spec/modules/kueezrtbBidAdapter_spec.js rename to test/spec/modules/kueezRtbBidAdapter_spec.js index 70ece966651..f9b2cd41a43 100644 --- a/test/spec/modules/kueezrtbBidAdapter_spec.js +++ b/test/spec/modules/kueezRtbBidAdapter_spec.js @@ -11,7 +11,7 @@ import { setStorageItem, tryParseJSON, getUniqueDealId, -} from 'modules/kueezrtbBidAdapter.js'; +} from 'modules/kueezRtbBidAdapter.js'; import * as utils from 'src/utils.js'; import {version} from 'package.json'; import {useFakeTimers} from 'sinon'; From 4656aa2af869c7556178e23a7229bf8c21cebd36 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 29 Nov 2022 16:03:10 -0500 Subject: [PATCH 490/569] Revert "AcuityAds adapter: fix issue with download (#9164)" (#9299) This reverts commit 4f21a5bc9e2e4ace066a4d26ca1c9c637c46a477. --- modules/{acuityadsBidAdapter.js => acuityAdsBidAdapter.js} | 0 modules/{acuityadsBidAdapter.md => acuityAdsBidAdapter.md} | 0 ...{acuityadsBidAdapter_spec.js => acuityAdsBidAdapter_spec.js} | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename modules/{acuityadsBidAdapter.js => acuityAdsBidAdapter.js} (100%) rename modules/{acuityadsBidAdapter.md => acuityAdsBidAdapter.md} (100%) rename test/spec/modules/{acuityadsBidAdapter_spec.js => acuityAdsBidAdapter_spec.js} (99%) diff --git a/modules/acuityadsBidAdapter.js b/modules/acuityAdsBidAdapter.js similarity index 100% rename from modules/acuityadsBidAdapter.js rename to modules/acuityAdsBidAdapter.js diff --git a/modules/acuityadsBidAdapter.md b/modules/acuityAdsBidAdapter.md similarity index 100% rename from modules/acuityadsBidAdapter.md rename to modules/acuityAdsBidAdapter.md diff --git a/test/spec/modules/acuityadsBidAdapter_spec.js b/test/spec/modules/acuityAdsBidAdapter_spec.js similarity index 99% rename from test/spec/modules/acuityadsBidAdapter_spec.js rename to test/spec/modules/acuityAdsBidAdapter_spec.js index 2497b435a19..18ea574c1ce 100644 --- a/test/spec/modules/acuityadsBidAdapter_spec.js +++ b/test/spec/modules/acuityAdsBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from '../../../modules/acuityadsBidAdapter'; +import { spec } from '../../../modules/acuityAdsBidAdapter'; import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; import { getUniqueIdentifierStr } from '../../../src/utils.js'; From 4f55dbc8c515378a709d1f7bcc34ba2e2d500542 Mon Sep 17 00:00:00 2001 From: kkho339 <119444562+kkho339@users.noreply.github.com> Date: Tue, 29 Nov 2022 13:52:05 -0800 Subject: [PATCH 491/569] Add new size 192x160 (ID: 622) in Rubicon Adapter (#9297) --- modules/rubiconBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 254799b995d..bd53e9d5104 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -134,7 +134,8 @@ var sizeMap = { 574: '620x891', 576: '610x877', 578: '980x552', - 580: '505x656' + 580: '505x656', + 622: '192x160' }; _each(sizeMap, (item, key) => sizeMap[item] = key); From de18e7ccab23bfa294f25bb4f691a8aaf6636ae0 Mon Sep 17 00:00:00 2001 From: redtram-tech <84892722+redtram-tech@users.noreply.github.com> Date: Wed, 30 Nov 2022 19:10:51 +0200 Subject: [PATCH 492/569] Redtram Bid Adapter : initial adapter release (#9260) * Add Redtram Bid Adapter * add on bidWon test * extend tests * remove convertOrtbRequestToProprietaryNative 9260#pullrequestreview-1196218534 Co-authored-by: Oleh Naimushyn --- modules/redtramBidAdapter.js | 155 ++++++++++++ modules/redtramBidAdapter.md | 33 +++ test/spec/modules/redtramBidAdapter_spec.js | 256 ++++++++++++++++++++ 3 files changed, 444 insertions(+) create mode 100644 modules/redtramBidAdapter.js create mode 100644 modules/redtramBidAdapter.md create mode 100644 test/spec/modules/redtramBidAdapter_spec.js diff --git a/modules/redtramBidAdapter.js b/modules/redtramBidAdapter.js new file mode 100644 index 00000000000..e1dc0e2a148 --- /dev/null +++ b/modules/redtramBidAdapter.js @@ -0,0 +1,155 @@ +import { + isFn, + isStr, + deepAccess, + getWindowTop, + triggerPixel +} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'redtram'; +const AD_URL = 'https://prebid.redtram.com/pbjs'; +const SYNC_URL = 'https://prebid.redtram.com/sync'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || + !bid.ttl || !bid.currency || !bid.meta) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + default: + return false; + } +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidFloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (_) { + return 0 + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && bid.params.placementId); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + const winTop = getWindowTop(); + const location = winTop.location; + const placements = []; + + const request = { + deviceWidth: winTop.screen.width, + deviceHeight: winTop.screen.height, + language: (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + host: location.host, + page: location.pathname, + placements: placements + }; + + if (bidderRequest) { + if (bidderRequest.uspConsent) { + request.ccpa = bidderRequest.uspConsent; + } + if (bidderRequest.gdprConsent) { + request.gdpr = bidderRequest.gdprConsent; + } + } + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + const placement = { + placementId: bid.params.placementId, + bidId: bid.bidId, + schain: bid.schain || {}, + bidfloor: getBidFloor(bid) + }; + + if (typeof bid.userId !== 'undefined') { + placement.userId = bid.userId; + } + + const mediaType = bid.mediaTypes; + + if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { + placement.sizes = mediaType[BANNER].sizes; + placement.adFormat = BANNER; + } + + placements.push(placement); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + }, + + onBidWon: (bid) => { + const cpm = deepAccess(bid, 'adserverTargeting.hb_pb') || ''; + if (isStr(bid.nurl) && bid.nurl !== '') { + bid.nurl = bid.nurl.replace(/\${AUCTION_PRICE}/, cpm); + triggerPixel(bid.nurl); + } + } +}; + +registerBidder(spec); diff --git a/modules/redtramBidAdapter.md b/modules/redtramBidAdapter.md new file mode 100644 index 00000000000..39115502aa7 --- /dev/null +++ b/modules/redtramBidAdapter.md @@ -0,0 +1,33 @@ +# Overview + +``` +Module Name: redtram Bidder Adapter +Module Type: redtram Bidder Adapter +Maintainer: support@redtram.com +``` + +# Description + +Module that connects to redtram demand sources + +# Test Parameters +``` + var adUnits = [ + { + code: 'div-prebid', + mediaTypes:{ + banner: { + sizes: [[300, 250]], + } + }, + bids:[ + { + bidder: 'redtram', + params: { + placementId: '23611' //test, please replace after test + } + } + ] + }, + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/redtramBidAdapter_spec.js b/test/spec/modules/redtramBidAdapter_spec.js new file mode 100644 index 00000000000..e136c37962b --- /dev/null +++ b/test/spec/modules/redtramBidAdapter_spec.js @@ -0,0 +1,256 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/redtramBidAdapter.js'; +import { BANNER } from '../../../src/mediaTypes.js'; +import * as utils from '../../../src/utils.js'; + +describe('RedtramBidAdapter', function () { + const bid = { + bidId: '23dc19818e5293', + bidder: 'redtram', + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 23611, + } + }; + + const bidderRequest = { + refererInfo: { + referer: 'test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + delete bid.params.placementId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid], bidderRequest); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://prebid.redtram.com/pbjs'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('placementId', 'bidId', 'adFormat', 'sizes', 'schain', 'bidfloor'); + expect(placement.placementId).to.equal(23611); + expect(placement.bidId).to.equal('23dc19818e5293'); + expect(placement.adFormat).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + expect(placement.sizes).to.be.an('array'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + bidderRequest.gdprConsent = 'test'; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = 'test'; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23dc19818e5293', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: {} + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23dc19818e5293'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.width).to.equal(300); + expect(dataItem.height).to.equal(250); + expect(dataItem.ad).to.equal('Test'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23dc19818e5293', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function () { + it('should do nothing on getUserSyncs', function () { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://prebid.redtram.com/sync/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + }); + + describe('on bidWon', function () { + beforeEach(function() { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function() { + utils.triggerPixel.restore(); + }); + it('should replace nurl for banner', function () { + const nurl = 'nurl/?ap=${' + 'AUCTION_PRICE}'; + const bid = { + 'bidderCode': 'redtram', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '5691dd18ba6ab6', + 'requestId': '23dc19818e5293', + 'transactionId': '948c716b-bf64-4303-bcf4-395c2f6a9770', + 'auctionId': 'a6b7c61f-15a9-481b-8f64-e859787e9c07', + 'mediaType': 'banner', + 'source': 'client', + 'ad': "
\n", + 'cpm': 0.68, + 'nurl': nurl, + 'creativeId': 'test', + 'currency': 'USD', + 'dealId': '', + 'meta': { + 'advertiserDomains': [], + 'dchain': { + 'ver': '1.0', + 'complete': 0, + 'nodes': [ + { + 'name': 'redtram' + } + ] + } + }, + 'netRevenue': true, + 'ttl': 120, + 'metrics': {}, + 'adapterCode': 'redtram', + 'originalCpm': 0.68, + 'originalCurrency': 'USD', + 'responseTimestamp': 1668162732297, + 'requestTimestamp': 1668162732292, + 'bidder': 'redtram', + 'adUnitCode': 'div-prebid', + 'timeToRespond': 5, + 'pbLg': '0.50', + 'pbMg': '0.60', + 'pbHg': '0.68', + 'pbAg': '0.65', + 'pbDg': '0.68', + 'pbCg': '', + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'redtram', + 'hb_adid': '5691dd18ba6ab6', + 'hb_pb': '0.68', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': '' + }, + 'status': 'rendered', + 'params': [ + { + 'placementId': 23611 + } + ] + }; + spec.onBidWon(bid); + expect(bid.nurl).to.deep.equal('nurl/?ap=0.68'); + }); + }); +}); From 5b13b541ff40cd704e81f630288a89585461fcb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Wed, 30 Nov 2022 19:11:23 +0200 Subject: [PATCH 493/569] VidazooBidAdapter: sending storageAllowed flag with request params (#9294) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * added bid params to request Co-authored-by: roman Co-authored-by: Saar Amrani <89377180+saar120@users.noreply.github.com> Co-authored-by: Saar Amrani --- modules/vidazooBidAdapter.js | 3 +++ test/spec/modules/vidazooBidAdapter_spec.js | 1 + 2 files changed, 4 insertions(+) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 39c407ae8a6..e79835b39c0 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -2,6 +2,7 @@ import { _each, deepAccess, parseSizesInput, parseUrl, uniques, isFn } from '../ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; +import { bidderSettings } from '../src/bidderSettings.js'; const GVLID = 744; const DEFAULT_SUB_DOMAIN = 'prebid'; @@ -69,6 +70,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const pId = extractPID(params); const subDomain = extractSubDomain(params); const ptrace = getCacheOpt(); + const isStorageAllowed = bidderSettings.get(BIDDER_CODE, 'storageAllowed'); const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); const cat = deepAccess(bidderRequest, 'ortb2.site.cat', []); @@ -104,6 +106,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { res: `${screen.width}x${screen.height}`, schain: schain, ptrace: ptrace, + isStorageAllowed: isStorageAllowed, gpid: gpid, cat: cat, pagecat: pagecat diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 7c7f27a830b..9c22b41968f 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -198,6 +198,7 @@ describe('VidazooBidAdapter', function () { uqs: getTopWindowQueryParams(), 'ext.param1': 'loremipsum', 'ext.param2': 'dolorsitamet', + isStorageAllowed: true, gpid: '1234567890', cat: ['IAB2'], pagecat: ['IAB2-2'] From 164e374b030f6f8f110ca22f419dccabf8170b1a Mon Sep 17 00:00:00 2001 From: Patrick Loughrey Date: Wed, 30 Nov 2022 12:13:54 -0500 Subject: [PATCH 494/569] Triplelift Adapter: Update referrer logic (#9304) * prioritize topmostlocation * adds test for topmostlocation / referrer * cleanup * delete param after test * TL-32803: Update referrer logic * TL-32803: Update referrer logic Co-authored-by: Nick Llerandi Co-authored-by: nllerandi3lift <75995508+nllerandi3lift@users.noreply.github.com> --- modules/tripleliftBidAdapter.js | 2 +- test/spec/modules/tripleliftBidAdapter_spec.js | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index 3fdabcfa6d2..7f6ec90c7b9 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -29,7 +29,7 @@ export const tripleliftAdapterSpec = { tlCall = tryAppendQueryString(tlCall, 'v', '$prebid.version$'); if (bidderRequest && bidderRequest.refererInfo) { - let referrer = bidderRequest?.refererInfo?.topmostLocation || bidderRequest?.refererInfo?.page; + let referrer = bidderRequest.refererInfo.page; tlCall = tryAppendQueryString(tlCall, 'referrer', referrer); } diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 4cab8be7e09..5cfa64184f9 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -979,19 +979,12 @@ describe('triplelift adapter', function () { expect(url).to.match(new RegExp('(?:' + prebid.version + ')')) expect(url).to.match(/(?:referrer)/); }); - it('should prioritize topmostLocation for referrer', function () { - bidderRequest.refererInfo.topmostLocation = 'https://topmostlocation.com?foo=bar' + it('should use refererInfo.page for referrer', function () { + bidderRequest.refererInfo.page = 'https://topmostlocation.com?foo=bar' const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); const url = request.url; expect(url).to.match(/(\?|&)referrer=https%3A%2F%2Ftopmostlocation.com%3Ffoo%3Dbar/); - delete bidderRequest.refererInfo.topmostLocation - }); - it('should fall back to page for referrer if topmostLocation is unavailable', function () { - bidderRequest.refererInfo.topmostLocation = null - const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); - const url = request.url; - expect(url).to.match(/(\?|&)referrer=https%3A%2F%2Fexamplereferer.com/); - delete bidderRequest.refererInfo.topmostLocation + delete bidderRequest.refererInfo.page }); it('should return us_privacy param when CCPA info is available', function() { bidderRequest.uspConsent = '1YYY'; From f8976951c1a2f3384c4c6a7cfb04c622017d13e4 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 30 Nov 2022 18:00:41 +0000 Subject: [PATCH 495/569] Prebid 7.27.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ed7cf9067e..15582191fc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.27.0-pre", + "version": "7.27.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index d243b650b6d..142dd2d1cdf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.27.0-pre", + "version": "7.27.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 1b4a5a356e74b6c0891e0e4d356f83b1be755d79 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 30 Nov 2022 18:00:42 +0000 Subject: [PATCH 496/569] Increment version to 7.28.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15582191fc2..8f5fd96e92a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.27.0", + "version": "7.28.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 142dd2d1cdf..bf52a74513a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.27.0", + "version": "7.28.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 0c71ee25e701b61c8c0e69fda282649350700f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ricardo=20Gielow?= Date: Wed, 30 Nov 2022 14:35:55 -0500 Subject: [PATCH 497/569] ttd Bid Adapter: add regression test topmost domain (#9300) * TTD Adapter use topmost location when available * TTD add use top most location regression test Co-authored-by: Andre Gielow --- test/helpers/refererDetectionHelper.js | 88 ++++++++++++++++++++++++ test/spec/modules/ttdBidAdapter_spec.js | 45 ++++++++++--- test/spec/refererDetection_spec.js | 89 +------------------------ 3 files changed, 125 insertions(+), 97 deletions(-) create mode 100644 test/helpers/refererDetectionHelper.js diff --git a/test/helpers/refererDetectionHelper.js b/test/helpers/refererDetectionHelper.js new file mode 100644 index 00000000000..bdbfb205cdb --- /dev/null +++ b/test/helpers/refererDetectionHelper.js @@ -0,0 +1,88 @@ +/** + * Build a walkable linked list of window-like objects for testing. + * + * @param {Array} urls Array of URL strings starting from the top window. + * @param {string} [topReferrer] + * @param {string} [canonicalUrl] + * @param {boolean} [ancestorOrigins] + * @returns {Object} + */ +export function buildWindowTree(urls, topReferrer = null, canonicalUrl = null, ancestorOrigins = false) { + /** + * Find the origin from a given fully-qualified URL. + * + * @param {string} url The fully qualified URL + * @returns {string|null} + */ + function getOrigin(url) { + const originRegex = new RegExp('^(https?://[^/]+/?)'); + + const result = originRegex.exec(url); + + if (result && result[0]) { + return result[0]; + } + + return null; + } + + const inaccessibles = []; + + let previousWindow, topWindow; + const topOrigin = getOrigin(urls[0]); + + const windowList = urls.map((url, index) => { + const thisOrigin = getOrigin(url), + sameOriginAsPrevious = index === 0 ? true : (getOrigin(urls[index - 1]) === thisOrigin), + sameOriginAsTop = thisOrigin === topOrigin; + + const win = { + location: { + href: url, + }, + document: { + referrer: index === 0 ? topReferrer : urls[index - 1] + } + }; + + if (topWindow == null) { + topWindow = win; + win.document.querySelector = function (selector) { + if (selector === 'link[rel=\'canonical\']') { + return { + href: canonicalUrl + }; + } + return null; + }; + } + + if (sameOriginAsPrevious) { + win.parent = previousWindow; + } else { + win.parent = inaccessibles[inaccessibles.length - 1]; + } + if (ancestorOrigins) { + win.location.ancestorOrigins = urls.slice(0, index).reverse().map(getOrigin); + } + win.top = sameOriginAsTop ? topWindow : inaccessibles[0]; + + const inWin = {parent: inaccessibles[inaccessibles.length - 1], top: inaccessibles[0]}; + if (index === 0) { + inWin.top = inWin; + } + ['document', 'location'].forEach((prop) => { + Object.defineProperty(inWin, prop, { + get: function () { + throw new Error('cross-origin access'); + } + }); + }); + inaccessibles.push(inWin); + previousWindow = win; + + return win; + }); + + return windowList[windowList.length - 1]; +} diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index f45872faec9..9869d072657 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -2,6 +2,9 @@ import { expect } from 'chai'; import { spec } from 'modules/ttdBidAdapter'; import { deepClone } from 'src/utils.js'; import { config } from 'src/config'; +import { detectReferer } from 'src/refererDetection.js'; + +import { buildWindowTree } from '../../helpers/refererDetectionHelper'; describe('ttdBidAdapter', function () { function testBuildRequests(bidRequests, bidderRequestBase) { @@ -200,20 +203,15 @@ describe('ttdBidAdapter', function () { 'bidRequestsCount': 1 }]; + const testWindow = buildWindowTree(['https://www.example.com/test', 'https://www.example.com/other/page', 'https://www.example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'); + const baseBidderRequestReferer = detectReferer(testWindow)(); const baseBidderRequest = { 'bidderCode': 'ttd', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', 'bidderRequestId': '18084284054531', 'auctionStart': 1540945362095, 'timeout': 3000, - 'refererInfo': { - 'page': 'https://www.example.com/test', - 'reachedTop': true, - 'numIframes': 0, - 'stack': [ - 'https://www.example.com/test' - ] - }, + 'refererInfo': baseBidderRequestReferer, 'start': 1540945362099, 'doneCbCallCount': 0 }; @@ -291,6 +289,11 @@ describe('ttdBidAdapter', function () { expect(requestBody.site.page).to.equal('https://www.example.com/test'); }); + it('ensure top most location is used', function () { + const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; + expect(requestBody.site.page).to.equal('https://www.example.com/test'); + }); + it('sets the banner pos correctly if sent', function () { let clonedBannerRequests = deepClone(baseBannerBidRequests); clonedBannerRequests[0].mediaTypes.banner.pos = 1; @@ -318,11 +321,35 @@ describe('ttdBidAdapter', function () { } } }; - const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + const baseBidderRequestWithoutRefererDomain = { + ...baseBidderRequest, + refererInfo: { + ...baseBannerBidRequests.referer, + domain: null + } + } + const requestBody = testBuildRequests( + baseBannerBidRequests, {...baseBidderRequestWithoutRefererDomain, ortb2} + ).data; config.resetConfig(); expect(requestBody.site.publisher).to.deep.equal({domain: 'https://foo.bar', id: '13144370'}); }); + it('referer domain overrides first party site data publisher domain', function () { + const ortb2 = { + site: { + publisher: { + domain: 'https://foo.bar', + } + } + }; + const requestBody = testBuildRequests( + baseBannerBidRequests, {...baseBidderRequest, ortb2} + ).data; + config.resetConfig(); + expect(requestBody.site.publisher.domain).to.equal(baseBidderRequest.refererInfo.domain); + }); + it('sets keywords properly if sent', function () { const ortb2 = { site: { diff --git a/test/spec/refererDetection_spec.js b/test/spec/refererDetection_spec.js index f8166a27d85..a0ffc3eddbe 100644 --- a/test/spec/refererDetection_spec.js +++ b/test/spec/refererDetection_spec.js @@ -2,94 +2,7 @@ import {detectReferer, ensureProtocol, parseDomain} from 'src/refererDetection.j import {config} from 'src/config.js'; import {expect} from 'chai'; -/** - * Build a walkable linked list of window-like objects for testing. - * - * @param {Array} urls Array of URL strings starting from the top window. - * @param {string} [topReferrer] - * @param {string} [canonicalUrl] - * @param {boolean} [ancestorOrigins] - * @returns {Object} - */ -function buildWindowTree(urls, topReferrer = null, canonicalUrl = null, ancestorOrigins = false) { - /** - * Find the origin from a given fully-qualified URL. - * - * @param {string} url The fully qualified URL - * @returns {string|null} - */ - function getOrigin(url) { - const originRegex = new RegExp('^(https?://[^/]+/?)'); - - const result = originRegex.exec(url); - - if (result && result[0]) { - return result[0]; - } - - return null; - } - - const inaccessibles = []; - - let previousWindow, topWindow; - const topOrigin = getOrigin(urls[0]); - - const windowList = urls.map((url, index) => { - const thisOrigin = getOrigin(url), - sameOriginAsPrevious = index === 0 ? true : (getOrigin(urls[index - 1]) === thisOrigin), - sameOriginAsTop = thisOrigin === topOrigin; - - const win = { - location: { - href: url, - }, - document: { - referrer: index === 0 ? topReferrer : urls[index - 1] - } - }; - - if (topWindow == null) { - topWindow = win; - win.document.querySelector = function (selector) { - if (selector === 'link[rel=\'canonical\']') { - return { - href: canonicalUrl - }; - } - return null; - }; - } - - if (sameOriginAsPrevious) { - win.parent = previousWindow; - } else { - win.parent = inaccessibles[inaccessibles.length - 1]; - } - if (ancestorOrigins) { - win.location.ancestorOrigins = urls.slice(0, index).reverse().map(getOrigin); - } - win.top = sameOriginAsTop ? topWindow : inaccessibles[0]; - - const inWin = {parent: inaccessibles[inaccessibles.length - 1], top: inaccessibles[0]}; - if (index === 0) { - inWin.top = inWin; - } - ['document', 'location'].forEach((prop) => { - Object.defineProperty(inWin, prop, { - get: function () { - throw new Error('cross-origin access'); - } - }); - }); - inaccessibles.push(inWin); - previousWindow = win; - - return win; - }); - - return windowList[windowList.length - 1]; -} +import { buildWindowTree } from '../helpers/refererDetectionHelper'; describe('Referer detection', () => { afterEach(function () { From 0725ba87275d0f88e534a94d8d4e0489b02c2d29 Mon Sep 17 00:00:00 2001 From: Jeremy Sadwith Date: Wed, 30 Nov 2022 17:39:15 -0500 Subject: [PATCH 498/569] Kargo Adapter: Update referrer logic (#9305) * pageURL pull from topmostLocation * Kargo: Support for client hints (#9) * Starting SUA support * Kargo: Adding support for client hints * Adding tests for sua * Kargo: Update referer logic --- modules/kargoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 15d706c0410..2c33c3f61d1 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -231,7 +231,7 @@ export const spec = { _getAllMetadata(bidderRequest, tdid) { return { userIDs: spec._getUserIds(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent), - pageURL: bidderRequest?.refererInfo?.topmostLocation || bidderRequest?.refererInfo?.page, + pageURL: bidderRequest?.refererInfo?.page, rawCRB: storage.getCookie('krg_crb'), rawCRBLocalStorage: spec._getLocalStorageSafely('krg_crb') }; From b95f26e523b6ec252aa05c0cb0f820a4c13bb903 Mon Sep 17 00:00:00 2001 From: BaronJHYu <254878848@qq.com> Date: Thu, 1 Dec 2022 23:44:47 +0800 Subject: [PATCH 499/569] Discovery Bid Adapter & Mediago Bid Adapter: add support for test request param (#9302) * Mediago Bid Adapter:new adapter * remove console * change spec file to fix CircleCI * change spec file to fix CircleCI * change spec file * Update mediagoBidAdapter.js * Update mediagoBidAdapter.js * rerun CurcleCi * update mediagoBidAdapter * update discoveryBidAdapter * Discovery Bid Adapter : parameter updates * Mediago Bid Adapter : parameter updates * Mediago Bid Adapter : code style format * rerun circleci * rerun circleci * rerun circleci * rerun circleci Co-authored-by: BaronYu --- modules/discoveryBidAdapter.js | 3 +++ modules/mediagoBidAdapter.js | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index fab3920efcc..0c536892716 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -277,6 +277,8 @@ function getItems(validBidRequests, bidderRequest) { function getParam(validBidRequests, bidderRequest) { const pubcid = utils.deepAccess(validBidRequests[0], 'crumbs.pubcid'); let isMobile = getDevice() ? 1 : 0; + // input test status by Publisher. more frequently for test true req + let isTest = validBidRequests[0].params.test || 0; let auctionId = getKv(bidderRequest, 'auctionId'); let items = getItems(validBidRequests, bidderRequest); @@ -290,6 +292,7 @@ function getParam(validBidRequests, bidderRequest) { if (items && items.length) { let c = { id: 'pp_hbjs_' + auctionId, + test: +isTest, at: 1, bcat: globals['bcat'], badv: globals['adv'], diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js index 514c56061df..16460b195f5 100644 --- a/modules/mediagoBidAdapter.js +++ b/modules/mediagoBidAdapter.js @@ -297,7 +297,8 @@ function getParam(validBidRequests, bidderRequest) { utils.deepAccess(validBidRequests[0], 'userId.sharedid.id') || utils.deepAccess(validBidRequests[0], 'userId.pubcid'); let isMobile = isMobileAndTablet() ? 1 : 0; - let isTest = 0; + // input test status by Publisher. more frequently for test true req + let isTest = validBidRequests[0].params.test || 0; let auctionId = getProperty(bidderRequest, 'auctionId'); let items = getItems(validBidRequests, bidderRequest); From 6b61f97333c24845a4ba187ae5d9c6c8d9b6774c Mon Sep 17 00:00:00 2001 From: Marcin Wrobel Date: Thu, 1 Dec 2022 18:14:38 +0100 Subject: [PATCH 500/569] OpenX Bid Adapter: update documentation about deprecated platform and hint for using floor module (#9308) --- modules/openxBidAdapter.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/openxBidAdapter.md b/modules/openxBidAdapter.md index 28dba424fb2..5ce5a786f7b 100644 --- a/modules/openxBidAdapter.md +++ b/modules/openxBidAdapter.md @@ -15,14 +15,15 @@ Publishers are welcome to test the other adapter and give feedback. Please note # Bid Parameters ## Banner -| Name | Scope | Type | Description | Example -| ---- | ----- | ---- | ----------- | ------- -| `delDomain` or `platform` | required | String | OpenX delivery domain or platform id provided by your OpenX representative. | "PUBLISHER-d.openx.net" or "555not5a-real-plat-form-id0123456789" -| `unit` | required | String | OpenX ad unit ID provided by your OpenX representative. | "1611023122" -| `customParams` | optional | Object | User-defined targeting key-value pairs. customParams applies to a specific unit. | `{key1: "v1", key2: ["v2","v3"]}` -| `customFloor` | optional | Number | Minimum price in USD. customFloor applies to a specific unit. For example, use the following value to set a $1.50 floor: 1.50

**WARNING:**
Misuse of this parameter can impact revenue | 1.50 -| `doNotTrack` | optional | Boolean | Prevents advertiser from using data for this user.

**WARNING:**
Request-level setting. May impact revenue. | true -| `coppa` | optional | Boolean | Enables Child's Online Privacy Protection Act (COPPA) regulations. | true +| Name | Scope | Type | Description | Example +|---------------------------------| ----- | ---- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------- +| `delDomain` ~~or `platform`~~** | required | String | OpenX delivery domain provided by your OpenX representative. | "PUBLISHER-d.openx.net" +| `unit` | required | String | OpenX ad unit ID provided by your OpenX representative. | "1611023122" +| `customParams` | optional | Object | User-defined targeting key-value pairs. customParams applies to a specific unit. | `{key1: "v1", key2: ["v2","v3"]}` +| `customFloor` | optional | Number | Minimum price in USD. customFloor applies to a specific unit. For example, use the following value to set a $1.50 floor: 1.50

**WARNING:**
Misuse of this parameter can impact revenue

Note: OpenX suggests using the [Price Floor Module](https://docs.prebid.org/dev-docs/modules/floors.html) instead of customFloor. The Price Floor Module will be prioritized over customFloor if both are present. | 1.50 +| `doNotTrack` | optional | Boolean | Prevents advertiser from using data for this user.

**WARNING:**
Request-level setting. May impact revenue. | true +| `coppa` | optional | Boolean | Enables Child's Online Privacy Protection Act (COPPA) regulations. | true +** platform is deprecated. Please use delDomain instead. If you have any questions please contact your representative. ## Video From 8894d1cd1a55076f2c8feae621fb3f5ad7d2e97b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 22:22:08 -0500 Subject: [PATCH 501/569] Bump decode-uri-component from 0.2.0 to 0.2.2 (#9311) Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2. - [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases) - [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2) --- updated-dependencies: - dependency-name: decode-uri-component dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8f5fd96e92a..f0b0b45a844 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "7.27.0-pre", + "version": "7.28.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -7986,9 +7986,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true, "engines": { "node": ">=0.10" @@ -31388,9 +31388,9 @@ } }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true }, "decompress-response": { From a4b4f7be8eb8d52764a32793cc0ef3eeae11d8c9 Mon Sep 17 00:00:00 2001 From: Marcin Wrobel Date: Fri, 2 Dec 2022 04:23:03 +0100 Subject: [PATCH 502/569] OpenX Bid Adapter: fix bid parameters table in documentation (#9310) --- modules/openxBidAdapter.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/openxBidAdapter.md b/modules/openxBidAdapter.md index 5ce5a786f7b..0690bf6b4fc 100644 --- a/modules/openxBidAdapter.md +++ b/modules/openxBidAdapter.md @@ -15,24 +15,24 @@ Publishers are welcome to test the other adapter and give feedback. Please note # Bid Parameters ## Banner -| Name | Scope | Type | Description | Example -|---------------------------------| ----- | ---- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------- -| `delDomain` ~~or `platform`~~** | required | String | OpenX delivery domain provided by your OpenX representative. | "PUBLISHER-d.openx.net" -| `unit` | required | String | OpenX ad unit ID provided by your OpenX representative. | "1611023122" -| `customParams` | optional | Object | User-defined targeting key-value pairs. customParams applies to a specific unit. | `{key1: "v1", key2: ["v2","v3"]}` -| `customFloor` | optional | Number | Minimum price in USD. customFloor applies to a specific unit. For example, use the following value to set a $1.50 floor: 1.50

**WARNING:**
Misuse of this parameter can impact revenue

Note: OpenX suggests using the [Price Floor Module](https://docs.prebid.org/dev-docs/modules/floors.html) instead of customFloor. The Price Floor Module will be prioritized over customFloor if both are present. | 1.50 -| `doNotTrack` | optional | Boolean | Prevents advertiser from using data for this user.

**WARNING:**
Request-level setting. May impact revenue. | true -| `coppa` | optional | Boolean | Enables Child's Online Privacy Protection Act (COPPA) regulations. | true +| Name | Scope | Type | Description | Example | +|---------------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------| +| `delDomain` ~~or `platform`~~** | required | String | OpenX delivery domain provided by your OpenX representative. | "PUBLISHER-d.openx.net" | +| `unit` | required | String | OpenX ad unit ID provided by your OpenX representative. | "1611023122" | +| `customParams` | optional | Object | User-defined targeting key-value pairs. customParams applies to a specific unit. | `{key1: "v1", key2: ["v2","v3"]}` | +| `customFloor` | optional | Number | Minimum price in USD. customFloor applies to a specific unit. For example, use the following value to set a $1.50 floor: 1.50

**WARNING:**
Misuse of this parameter can impact revenue

Note: OpenX suggests using the [Price Floor Module](https://docs.prebid.org/dev-docs/modules/floors.html) instead of customFloor. The Price Floor Module is prioritized over customFloor if both are present. | 1.50 | +| `doNotTrack` | optional | Boolean | Prevents advertiser from using data for this user.

**WARNING:**
Request-level setting. May impact revenue. | true | +| `coppa` | optional | Boolean | Enables Child's Online Privacy Protection Act (COPPA) regulations. | true | + ** platform is deprecated. Please use delDomain instead. If you have any questions please contact your representative. ## Video -| Name | Scope | Type | Description | Example -| ---- | ----- | ---- | ----------- | ------- -| `unit` | required | String | OpenX ad unit ID provided by your OpenX representative. | "1611023122" -| `delDomain` | required | String | OpenX delivery domain provided by your OpenX representative. | "PUBLISHER-d.openx.net" -| `openrtb` | optional | OpenRTB Impression | An OpenRtb Impression with Video subtype properties | `{ imp: [{ video: {mimes: ['video/x-ms-wmv, video/mp4']} }] }` - +| Name | Scope | Type | Description | Example | +|-------------|----------|--------------------|--------------------------------------------------------------|----------------------------------------------------------------| +| `unit` | required | String | OpenX ad unit ID provided by your OpenX representative. | "1611023122" | +| `delDomain` | required | String | OpenX delivery domain provided by your OpenX representative. | "PUBLISHER-d.openx.net" | +| `openrtb` | optional | OpenRTB Impression | An OpenRtb Impression with Video subtype properties | `{ imp: [{ video: {mimes: ['video/x-ms-wmv, video/mp4']} }] }` | # Example ```javascript From a0cc68db28632f8bcc5e695105b697dcc6128e99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 07:27:35 -0700 Subject: [PATCH 503/569] Bump tibdex/github-app-token from 1.6.0 to 1.7.0 (#9316) Bumps [tibdex/github-app-token](https://github.com/tibdex/github-app-token) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/tibdex/github-app-token/releases) - [Commits](https://github.com/tibdex/github-app-token/compare/f717b5ecd4534d3c4df4ce9b5c1c2214f0f7cd06...021a2405c7f990db57f5eae5397423dcc554159c) --- updated-dependencies: - dependency-name: tibdex/github-app-token dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/issue_tracker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue_tracker.yml b/.github/workflows/issue_tracker.yml index 05d08b2b0d7..4a9502e38c1 100644 --- a/.github/workflows/issue_tracker.yml +++ b/.github/workflows/issue_tracker.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Generate token id: generate_token - uses: tibdex/github-app-token@f717b5ecd4534d3c4df4ce9b5c1c2214f0f7cd06 + uses: tibdex/github-app-token@021a2405c7f990db57f5eae5397423dcc554159c with: app_id: ${{ secrets.ISSUE_APP_ID }} private_key: ${{ secrets.ISSUE_APP_PEM }} From 1c63ed92a1435244a2fd08fa491ba22c636165ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Tue, 6 Dec 2022 18:06:14 +0200 Subject: [PATCH 504/569] Vidazoo Bid Adapter: support for Video MediaTypes (#9284) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(client): added VIDEO media type * feat(client): send mediaTypes to the server * feat(client): added vastXml support * fix(client): vidazoo adapter tests * fix tests * remove console.log from test file * added video tests Co-authored-by: roman --- modules/vidazooBidAdapter.js | 27 +++- test/spec/modules/vidazooBidAdapter_spec.js | 136 +++++++++++++++++++- 2 files changed, 153 insertions(+), 10 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index e79835b39c0..bb3e4abd838 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,6 +1,6 @@ import { _each, deepAccess, parseSizesInput, parseUrl, uniques, isFn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; import { bidderSettings } from '../src/bidderSettings.js'; @@ -59,7 +59,7 @@ function isBidRequestValid(bid) { } function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { - const { params, bidId, userId, adUnitCode, schain } = bid; + const { params, bidId, userId, adUnitCode, schain, mediaTypes } = bid; const { ext } = params; let { bidFloor } = params; const hashUrl = hashCode(topWindowUrl); @@ -105,6 +105,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { prebidVersion: '$prebid.version$', res: `${screen.width}x${screen.height}`, schain: schain, + mediaTypes: mediaTypes, ptrace: ptrace, isStorageAllowed: isStorageAllowed, gpid: gpid, @@ -188,11 +189,12 @@ function interpretResponse(serverResponse, request) { try { results.forEach(result => { - const { creativeId, ad, price, exp, width, height, currency, advertiserDomains } = result; + const { creativeId, ad, price, exp, width, height, currency, advertiserDomains, mediaType = BANNER } = result; if (!ad || !price) { return; } - output.push({ + + const response = { requestId: bidId, cpm: price, width: width, @@ -201,11 +203,22 @@ function interpretResponse(serverResponse, request) { currency: currency || CURRENCY, netRevenue: true, ttl: exp || TTL_SECONDS, - ad: ad, meta: { advertiserDomains: advertiserDomains || [] } - }) + }; + + if (mediaType === BANNER) { + Object.assign(response, { + ad: ad, + }); + } else { + Object.assign(response, { + vastXml: ad, + mediaType: VIDEO + }); + } + output.push(response); }); return output; } catch (e) { @@ -322,7 +335,7 @@ export const spec = { code: BIDDER_CODE, version: BIDDER_VERSION, gvlid: GVLID, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid, buildRequests, interpretResponse, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 9c22b41968f..40aa7641330 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -17,6 +17,7 @@ import { import * as utils from 'src/utils.js'; import { version } from 'package.json'; import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes'; const SUB_DOMAIN = 'openrtb'; @@ -39,6 +40,7 @@ const BID = { 'bidderRequestId': '1fdb5ff1b6eaa7', 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'mediaTypes': [BANNER], 'ortb2Imp': { 'ext': { 'gpid': '1234567890' @@ -46,6 +48,38 @@ const BID = { } }; +const VIDEO_BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', + 'bidderRequestId': '12a8ae9ada9c13', + 'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee', + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '635509f7ff6642d368cb9837', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1 + }, + 'sizes': [[545, 307]], + 'mediaTypes': { + 'video': { + 'playerSize': [[545, 307]], + 'context': 'instream', + 'mimes': [ + 'video/mp4', + 'application/javascript' + ], + 'protocols': [2, 3, 5, 6], + 'maxduration': 60, + 'minduration': 0, + 'startdelay': 0, + 'linearity': 1, + 'api': [2], + 'placement': 1 + } + } +} + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -86,6 +120,23 @@ const SERVER_RESPONSE = { } }; +const VIDEO_SERVER_RESPONSE = { + body: { + 'cid': '635509f7ff6642d368cb9837', + 'results': [{ + 'ad': '', + 'advertiserDomains': ['vidazoo.com'], + 'exp': 60, + 'width': 545, + 'height': 307, + 'mediaType': 'video', + 'creativeId': '12610997325162499419', + 'price': 2, + 'cookies': [] + }] + } +} + const REQUEST = { data: { width: 300, @@ -124,6 +175,11 @@ describe('VidazooBidAdapter', function () { it('exists and is a string', function () { expect(adapter.code).to.exist.and.to.be.a('string'); }); + + it('exists and contains media types', function () { + expect(adapter.supportedMediaTypes).to.exist.and.to.be.an('array').with.length(2); + expect(adapter.supportedMediaTypes).to.contain.members([BANNER, VIDEO]); + }); }); describe('validate bid requests', function () { @@ -168,7 +224,59 @@ describe('VidazooBidAdapter', function () { sandbox.stub(Date, 'now').returns(1000); }); - it('should build request for each size', function () { + it('should build video request', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + const requests = adapter.buildRequests([VIDEO_BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/635509f7ff6642d368cb9837`, + data: { + adUnitCode: '63550ad1ff6642d368cba59dh5884270560', + bidFloor: 0.1, + bidId: '2d52001cabd527', + bidderVersion: adapter.version, + cat: ['IAB2'], + pagecat: ['IAB2-2'], + cb: 1000, + dealId: 1, + gdpr: 1, + gdprConsent: 'consent_string', + usPrivacy: 'consent_string', + gpid: '', + prebidVersion: version, + ptrace: '1000', + publisherId: '59ac17c192832d0011283fe3', + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + res: `${window.top.screen.width}x${window.top.screen.height}`, + schain: VIDEO_BID.schain, + sessionId: '', + sizes: ['545x307'], + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + uqs: getTopWindowQueryParams(), + mediaTypes: { + video: { + api: [2], + context: 'instream', + linearity: 1, + maxduration: 60, + mimes: [ + 'video/mp4', + 'application/javascript' + ], + minduration: 0, + placement: 1, + playerSize: [[545, 307]], + protocols: [2, 3, 5, 6], + startdelay: 0 + } + } + } + }); + }); + + it('should build banner request for each size', function () { const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); const requests = adapter.buildRequests([BID], BIDDER_REQUEST); expect(requests).to.have.length(1); @@ -187,7 +295,7 @@ describe('VidazooBidAdapter', function () { bidId: '2d52001cabd527', adUnitCode: 'div-gpt-ad-12345-0', publisherId: '59ac17c192832d0011283fe3', - dealId: 1, + dealId: 2, sessionId: '', uniqueDealId: `${hashUrl}_${Date.now().toString()}`, bidderVersion: adapter.version, @@ -195,6 +303,7 @@ describe('VidazooBidAdapter', function () { schain: BID.schain, ptrace: '1000', res: `${window.top.screen.width}x${window.top.screen.height}`, + mediaTypes: [BANNER], uqs: getTopWindowQueryParams(), 'ext.param1': 'loremipsum', 'ext.param2': 'dolorsitamet', @@ -211,6 +320,7 @@ describe('VidazooBidAdapter', function () { sandbox.restore(); }); }); + describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); @@ -255,7 +365,7 @@ describe('VidazooBidAdapter', function () { expect(responses).to.be.empty; }); - it('should return an array of interpreted responses', function () { + it('should return an array of interpreted banner responses', function () { const responses = adapter.interpretResponse(SERVER_RESPONSE, REQUEST); expect(responses).to.have.length(1); expect(responses[0]).to.deep.equal({ @@ -274,6 +384,26 @@ describe('VidazooBidAdapter', function () { }); }); + it('should return an array of interpreted video responses', function () { + const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 2, + width: 545, + height: 307, + mediaType: 'video', + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 60, + vastXml: '', + meta: { + advertiserDomains: ['vidazoo.com'] + } + }); + }); + it('should take default TTL', function () { const serverResponse = utils.deepClone(SERVER_RESPONSE); delete serverResponse.body.results[0].exp; From a0e6e6bec2e14a1e7c0bae4fb0b13913ef70fed1 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 6 Dec 2022 11:37:47 -0700 Subject: [PATCH 505/569] Vidazoo bid adapter: fix failing test (#9318) --- test/spec/modules/vidazooBidAdapter_spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 40aa7641330..2ddb65469af 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -255,6 +255,7 @@ describe('VidazooBidAdapter', function () { sizes: ['545x307'], uniqueDealId: `${hashUrl}_${Date.now().toString()}`, uqs: getTopWindowQueryParams(), + isStorageAllowed: true, mediaTypes: { video: { api: [2], From 4515eb343d2ff348fc3965d064ebdd63f40a2061 Mon Sep 17 00:00:00 2001 From: Wiem Zine El Abidine Date: Tue, 6 Dec 2022 22:12:55 +0100 Subject: [PATCH 506/569] Live Intent User ID Submodule: Bump live-connect version (#9317) * update with live-connect last change * set globalVarName * not use globalVarName * use live-connect proper version * comment * use yalc version and adjust the initializer * adjust getInitializer * use a proper lc version --- modules/liveIntentIdSystem.js | 3 +-- package-lock.json | 14 +++++++------- package.json | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index 724bf5ece6a..de70b0eaccd 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -10,7 +10,6 @@ import { submodule } from '../src/hook.js'; import { LiveConnect } from 'live-connect-js/esm/initializer.js'; import { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; -import { MinimalLiveConnect } from 'live-connect-js/esm/minimal-live-connect.js'; const MODULE_NAME = 'liveIntentId'; export const storage = getStorageManager({gvlid: null, moduleName: MODULE_NAME}); @@ -140,7 +139,7 @@ export const liveIntentIdSubmodule = { this.moduleMode = mode }, getInitializer() { - return this.moduleMode === 'minimal' ? MinimalLiveConnect : LiveConnect + return (liveConnectConfig, storage, calls) => LiveConnect(liveConnectConfig, storage, calls, this.moduleMode) }, /** diff --git a/package-lock.json b/package-lock.json index f0b0b45a844..f73e700aa42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "express": "^4.15.4", "fun-hooks": "^0.9.9", "just-clone": "^1.0.2", - "live-connect-js": "2.4.0" + "live-connect-js": "3.0.1" }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", @@ -16229,9 +16229,9 @@ "dev": true }, "node_modules/live-connect-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-2.4.0.tgz", - "integrity": "sha512-MSBLKfnXoxH+pqwji/Mf8yZu3VZMq4tnNfwMw7NTWN5a+TBM6f0RWgwui1YMA3nHmMhX/nzxxsso0SkyKcF0fA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-3.0.1.tgz", + "integrity": "sha512-rwB0IQfuKPJM+I96nLyq8Utr3LkQ7Z/iuq/xKlWDckQRJLYyWkk7F7yaavf/VsjazzLK2dpJeXGijoDkK4Vz8g==", "dependencies": { "tiny-hashes": "1.0.1" }, @@ -37868,9 +37868,9 @@ "dev": true }, "live-connect-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-2.4.0.tgz", - "integrity": "sha512-MSBLKfnXoxH+pqwji/Mf8yZu3VZMq4tnNfwMw7NTWN5a+TBM6f0RWgwui1YMA3nHmMhX/nzxxsso0SkyKcF0fA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-3.0.1.tgz", + "integrity": "sha512-rwB0IQfuKPJM+I96nLyq8Utr3LkQ7Z/iuq/xKlWDckQRJLYyWkk7F7yaavf/VsjazzLK2dpJeXGijoDkK4Vz8g==", "requires": { "tiny-hashes": "1.0.1" } diff --git a/package.json b/package.json index bf52a74513a..7bbe18037b4 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "express": "^4.15.4", "fun-hooks": "^0.9.9", "just-clone": "^1.0.2", - "live-connect-js": "2.4.0" + "live-connect-js": "3.0.1" }, "optionalDependencies": { "fsevents": "^2.3.2" From a247abc624f0acd3a5bc6af9ebb785d8555f0ab2 Mon Sep 17 00:00:00 2001 From: Vic R <103455651+victorlassomarketing@users.noreply.github.com> Date: Wed, 7 Dec 2022 12:16:37 -0800 Subject: [PATCH 507/569] ox update (#9309) --- modules/lassoBidAdapter.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/lassoBidAdapter.js b/modules/lassoBidAdapter.js index da48bc460c7..fafaa4dd9dc 100644 --- a/modules/lassoBidAdapter.js +++ b/modules/lassoBidAdapter.js @@ -54,7 +54,7 @@ export const spec = { return { method: 'GET', - url: getBidRequestUrl(aimXR), + url: getBidRequestUrl(aimXR, bidRequest.params), data: payload, options: { withCredentials: true @@ -113,11 +113,15 @@ export const spec = { supportedMediaTypes: [BANNER] } -function getBidRequestUrl(aimXR) { +function getBidRequestUrl(aimXR, params) { + let path = '/request'; + if (params && params.dtc) { + path = '/dtc-request'; + } if (!aimXR) { - return GET_IUD_URL + ENDPOINT_URL + '/request'; + return GET_IUD_URL + ENDPOINT_URL + path; } - return ENDPOINT_URL + '/request' + return ENDPOINT_URL + path; } function getDeviceData() { From 2a90f5165b19b6a5e8ab01be76b2e19da69f97cc Mon Sep 17 00:00:00 2001 From: Damyan Date: Thu, 8 Dec 2022 10:26:56 +0200 Subject: [PATCH 508/569] AdHash bid adapter: update to support latest version (#9286) * AdHash Bidder Adapter: minor changes We're operating on a com TLD now. Added publisher in URL for easier routing. * Implemented brand safety Implemented brand safety checks * Fix for GDPR consent Removing the extra information as request data becomes too big and is sometimes truncated * Ad fraud prevention formula changed Ad fraud prevention formula changed to support negative values as well as linear distribution of article length * AdHash brand safety additions Adding starts-with and ends-with rules that will help us with languages such as German where a single word can be written in multiple ways depending on the gender and grammatical case. * AdHash brand safety updates Added support for Cyrillic characters. Added support for bidderURL parameter. Fixed score multiplier from 500 to 1000. * AdHash Analytics adapter * Support for recent ads Support for recent ads which gives us the option to do frequency and recency capping. * Fix for timestamp * PUB-222 Added logic for measuring the fill rate (fallbacks) for Prebid impressions * Unit tests for the analytics adapter Added unit tests for the analytics adapter * Removed export causing errors Removed an unneeded export of a const that was causing errors with the analytics adapter * Added globalScript parameter * PUB-227 Support for non-latin and non-cyrillic symbols * GEN-964 - Brand safety now checks the page URL for bad words. No ad is shown if there is at least one match. - Repeating code is optimized and moved to helper function - Multi-language support for brand safety * GEN-1025 Sending the needed ad density data to the bidder * Removing the analytics adaptor * Fix for regexp match * Version change * MINOR Code review changes Co-authored-by: NikolayMGeorgiev Co-authored-by: Ventsislav Saraminev Co-authored-by: Dimitar Kalenderov --- modules/adhashBidAdapter.js | 145 ++++++++++++++++----- modules/adhashBidAdapter.md | 4 +- test/spec/modules/adhashBidAdapter_spec.js | 76 +++++++++-- 3 files changed, 181 insertions(+), 44 deletions(-) diff --git a/modules/adhashBidAdapter.js b/modules/adhashBidAdapter.js index 977d161b214..33a85a81525 100644 --- a/modules/adhashBidAdapter.js +++ b/modules/adhashBidAdapter.js @@ -1,10 +1,12 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {includes} from '../src/polyfill.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { includes } from '../src/polyfill.js'; +import { BANNER } from '../src/mediaTypes.js'; -const VERSION = '1.0'; +const VERSION = '3.2'; const BAD_WORD_STEP = 0.1; const BAD_WORD_MIN = 0.2; +const ADHASH_BIDDER_CODE = 'adhash'; /** * Function that checks the page where the ads are being served for brand safety. @@ -54,43 +56,92 @@ function brandSafety(badWords, maxScore) { return positive ? result : -result; }; + /** + * Checks what rule will match in the given array with words + * @param {string} rule rule type (full, partial, starts, ends, regexp) + * @param {string} decodedWord decoded word + * @param {array} wordsToMatch array to find a match + * @returns {object|boolean} matched rule and occurances. If nothing is matched returns false + */ + const wordsMatchedWithRule = function (rule, decodedWord, wordsToMatch) { + if (rule === 'full' && wordsToMatch && wordsToMatch.includes(decodedWord)) { + return { rule, occurances: wordsToMatch.filter(element => element === decodedWord).length }; + } else if (rule === 'partial' && wordsToMatch && wordsToMatch.some(element => element.indexOf(decodedWord) > -1)) { + return { rule, occurances: wordsToMatch.filter(element => element.indexOf(decodedWord) > -1).length }; + } else if (rule === 'starts' && wordsToMatch && wordsToMatch.some(word => word.startsWith(decodedWord))) { + return { rule, occurances: wordsToMatch.filter(element => element.startsWith(decodedWord)).length }; + } else if (rule === 'ends' && wordsToMatch && wordsToMatch.some(word => word.endsWith(decodedWord))) { + return { rule, occurances: wordsToMatch.filter(element => element.endsWith(decodedWord)).length }; + } else if (rule === 'regexp' && wordsToMatch && wordsToMatch.some(element => element.match(new RegExp(decodedWord, 'i')))) { + return { rule, occurances: wordsToMatch.filter(element => element.match(new RegExp(decodedWord, 'i'))).length }; + } + return false; + }; + // Default parameters if the bidder is unable to send some of them badWords = badWords || []; maxScore = parseInt(maxScore) || 10; try { let score = 0; + const decodedUrl = decodeURI(window.top.location.href.substring(window.top.location.origin.length)); + const wordsAndNumbersInUrl = decodedUrl + .replaceAll(/[-,\._/\?=&#%]/g, ' ') + .replaceAll(/\s\s+/g, ' ') + .toLowerCase() + .trim(); const content = window.top.document.body.innerText.toLowerCase(); - const words = content.trim().split(/\s+/).length; + const contentWords = content.trim().split(/\s+/).length; + // \p{L} matches a single unicode code point in the category 'letter'. Matches any kind of letter from any language. + const regexp = new RegExp('[\\p{L}]+', 'gu'); + const words = content.match(regexp); + const wordsInUrl = wordsAndNumbersInUrl.match(regexp); + for (const [word, rule, points] of badWords) { - if (rule === 'full' && new RegExp('\\b' + rot13(word) + '\\b', 'i').test(content)) { - const occurances = content.match(new RegExp('\\b' + rot13(word) + '\\b', 'g')).length; - score += scoreCalculator(points, occurances); - } else if (rule === 'partial' && content.indexOf(rot13(word.toLowerCase())) > -1) { - const occurances = content.match(new RegExp(rot13(word), 'g')).length; - score += scoreCalculator(points, occurances); + const decodedWord = rot13(word.toLowerCase()); + + // Checks the words in the url of the page only for negative words. Don't serve any ad when at least one match is found + if (points > 0) { + const matchedRuleInUrl = wordsMatchedWithRule(rule, decodedWord, wordsInUrl); + if (matchedRuleInUrl.rule) { + return false; + } + } + + // Check if site content's words match any of our brand safety rules + const matchedRule = wordsMatchedWithRule(rule, decodedWord, words); + if (matchedRule.rule === 'full') { + score += scoreCalculator(points, matchedRule.occurances); + } else if (matchedRule.rule === 'partial') { + score += scoreCalculator(points, matchedRule.occurances); + } else if (matchedRule.rule === 'starts') { + score += scoreCalculator(points, matchedRule.occurances); + } else if (matchedRule.rule === 'ends') { + score += scoreCalculator(points, matchedRule.occurances); + } else if (matchedRule.rule === 'regexp') { + score += scoreCalculator(points, matchedRule.occurances); } } - return score < maxScore * words / 500; + return score < (maxScore * contentWords) / 1000; } catch (e) { return true; } } export const spec = { - code: 'adhash', - url: 'https://bidder.adhash.com/rtb?version=' + VERSION + '&prebid=true', + code: ADHASH_BIDDER_CODE, supportedMediaTypes: [ BANNER ], isBidRequestValid: (bid) => { try { - const { publisherId, platformURL } = bid.params; + const { publisherId, platformURL, bidderURL } = bid.params; return ( includes(Object.keys(bid.mediaTypes), BANNER) && typeof publisherId === 'string' && publisherId.length === 42 && typeof platformURL === 'string' && - platformURL.length >= 13 + platformURL.length >= 13 && + (!bidderURL || bidderURL.indexOf('https://') === 0) ); } catch (error) { return false; @@ -98,24 +149,51 @@ export const spec = { }, buildRequests: (validBidRequests, bidderRequest) => { + const storage = getStorageManager({ bidderCode: ADHASH_BIDDER_CODE }); const { gdprConsent } = bidderRequest; - const { url } = spec; const bidRequests = []; - let referrer = ''; - if (bidderRequest && bidderRequest.refererInfo) { - // TODO: is 'page' the right value here? - referrer = bidderRequest.refererInfo.page; - } - for (var i = 0; i < validBidRequests.length; i++) { - var index = Math.floor(Math.random() * validBidRequests[i].sizes.length); - var size = validBidRequests[i].sizes[index].join('x'); + const body = document.body; + const html = document.documentElement; + const pageHeight = Math.max( + body.scrollHeight, + body.offsetHeight, + html.clientHeight, + html.scrollHeight, + html.offsetHeight + ); + const pageWidth = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth); + + for (let i = 0; i < validBidRequests.length; i++) { + const bidderURL = validBidRequests[i].params.bidderURL || 'https://bidder.adhash.com'; + const url = `${bidderURL}/rtb?version=${VERSION}&prebid=true`; + const index = Math.floor(Math.random() * validBidRequests[i].sizes.length); + const size = validBidRequests[i].sizes[index].join('x'); + + let recentAds = []; + if (storage.localStorageIsEnabled()) { + const prefix = validBidRequests[i].params.prefix || 'adHash'; + recentAds = JSON.parse(storage.getDataFromLocalStorage(prefix + 'recentAds') || '[]'); + } + + // Needed for the ad density calculation + var adHeight = validBidRequests[i].sizes[index][1]; + var adWidth = validBidRequests[i].sizes[index][0]; + if (!window.adsCount) { + window.adsCount = 0; + } + if (!window.adsTotalSurface) { + window.adsTotalSurface = 0; + } + window.adsTotalSurface += adHeight * adWidth; + window.adsCount++; + bidRequests.push({ method: 'POST', url: url + '&publisher=' + validBidRequests[i].params.publisherId, bidRequest: validBidRequests[i], data: { timezone: new Date().getTimezoneOffset() / 60, - location: referrer, + location: bidderRequest.refererInfo ? bidderRequest.refererInfo.topmostLocation : '', publisherId: validBidRequests[i].params.publisherId, size: { screenWidth: window.screen.width, @@ -131,10 +209,14 @@ export const spec = { position: validBidRequests[i].adUnitCode }], blockedCreatives: [], - currentTimestamp: new Date().getTime(), - recentAds: [], + currentTimestamp: (new Date().getTime() / 1000) | 0, + recentAds: recentAds, GDPRApplies: gdprConsent ? gdprConsent.gdprApplies : null, - GDPR: gdprConsent ? gdprConsent.consentString : null + GDPR: gdprConsent ? gdprConsent.consentString : null, + servedAdsCount: window.adsCount, + adsTotalSurface: window.adsTotalSurface, + pageHeight: pageHeight, + pageWidth: pageWidth }, options: { withCredentials: false, @@ -157,7 +239,11 @@ export const spec = { } const publisherURL = JSON.stringify(request.bidRequest.params.platformURL); + const bidderURL = request.bidRequest.params.bidderURL || 'https://bidder.adhash.com'; const oneTimeId = request.bidRequest.adUnitCode + Math.random().toFixed(16).replace('0.', '.'); + const globalScript = !request.bidRequest.params.globalScript + ? `` + : ''; const bidderResponse = JSON.stringify({ responseText: JSON.stringify(responseBody) }); const requestData = JSON.stringify(request.data); @@ -165,8 +251,7 @@ export const spec = { requestId: request.bidRequest.bidId, cpm: responseBody.creatives[0].costEUR, ad: - `
- + `
${globalScript} `, width: request.bidRequest.sizes[0][0], height: request.bidRequest.sizes[0][1], diff --git a/modules/adhashBidAdapter.md b/modules/adhashBidAdapter.md index 4ee6ed3dc83..acca5a1e651 100644 --- a/modules/adhashBidAdapter.md +++ b/modules/adhashBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: AdHash Bidder Adapter Module Type: Bidder Adapter -Maintainer: damyan@adhash.org +Maintainer: damyan@adhash.com ``` # Description @@ -14,8 +14,6 @@ Here is what you need for Prebid integration with AdHash: 3. Use the Publisher ID and Platform URL as parameters in params. Please note that a number of AdHash functionalities are not supported in the Prebid.js integration: -* Cookie-less frequency and recency capping; -* Audience segments; * Price floors and passback tags, as they are not needed in the Prebid.js setup; * Reservation for direct deals only, as bids are evaluated based on their price. diff --git a/test/spec/modules/adhashBidAdapter_spec.js b/test/spec/modules/adhashBidAdapter_spec.js index 40bf354c4d9..4ea525c59d5 100644 --- a/test/spec/modules/adhashBidAdapter_spec.js +++ b/test/spec/modules/adhashBidAdapter_spec.js @@ -60,6 +60,12 @@ describe('adhashBidAdapter', function () { bid.params.platformURL = 'https://'; expect(spec.isBidRequestValid(bid)).to.equal(false); }); + + it('should return false when bidderURL is present but not https://', function () { + const bid = { ...validBid }; + bid.params.bidderURL = 'http://example.com/'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); }); describe('buildRequests', function () { @@ -73,11 +79,11 @@ describe('adhashBidAdapter', function () { it('should build the request correctly', function () { const result = spec.buildRequests( [ bidRequest ], - { gdprConsent: { gdprApplies: true, consentString: 'example' }, refererInfo: { referer: 'http://example.com/' } } + { gdprConsent: { gdprApplies: true, consentString: 'example' }, refererInfo: { topmostLocation: 'https://example.com/path.html' } } ); expect(result.length).to.equal(1); expect(result[0].method).to.equal('POST'); - expect(result[0].url).to.equal('https://bidder.adhash.com/rtb?version=1.0&prebid=true&publisher=0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb'); + expect(result[0].url).to.equal('https://bidder.adhash.com/rtb?version=3.2&prebid=true&publisher=0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb'); expect(result[0].bidRequest).to.equal(bidRequest); expect(result[0].data).to.have.property('timezone'); expect(result[0].data).to.have.property('location'); @@ -93,7 +99,7 @@ describe('adhashBidAdapter', function () { const result = spec.buildRequests([ bidRequest ], { gdprConsent: { gdprApplies: true, consentString: 'example' } }); expect(result.length).to.equal(1); expect(result[0].method).to.equal('POST'); - expect(result[0].url).to.equal('https://bidder.adhash.com/rtb?version=1.0&prebid=true&publisher=0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb'); + expect(result[0].url).to.equal('https://bidder.adhash.com/rtb?version=3.2&prebid=true&publisher=0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb'); expect(result[0].bidRequest).to.equal(bidRequest); expect(result[0].data).to.have.property('timezone'); expect(result[0].data).to.have.property('location'); @@ -127,9 +133,15 @@ describe('adhashBidAdapter', function () { creatives: [{ costEUR: 1.234 }], advertiserDomains: 'adhash.com', badWords: [ - ['onqjbeq1', 'full', 1], - ['onqjbeq2', 'partial', 1], + ['onqjbeq', 'full', 1], + ['onqjbeqo', 'partial', 1], ['tbbqjbeq', 'full', -1], + ['fgnegf', 'starts', 1], + ['raqf', 'ends', 1], + ['kkk[no]lll', 'regexp', 1], + ['дума', 'full', 1], + ['старт', 'starts', 1], + ['край', 'ends', 1], ], maxScore: 2 } @@ -155,42 +167,84 @@ describe('adhashBidAdapter', function () { it('should return empty array when there are bad words (full)', function () { bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { - return 'example text badWord1 badWord1 example badWord1 text' + ' word'.repeat(493); + return 'example text badword badword example badword text' + ' word'.repeat(993); + }); + expect(spec.interpretResponse(serverResponse, request).length).to.equal(0); + }); + + it('should return empty array when there are bad words (full cyrillic)', function () { + bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { + return 'example text дума дума example дума text' + ' текст'.repeat(993); }); expect(spec.interpretResponse(serverResponse, request).length).to.equal(0); }); it('should return empty array when there are bad words (partial)', function () { bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { - return 'example text partialBadWord2 badword2 example BadWord2text' + ' word'.repeat(494); + return 'example text partialbadwordb badwordb example badwordbtext' + ' word'.repeat(994); + }); + expect(spec.interpretResponse(serverResponse, request).length).to.equal(0); + }); + + it('should return empty array when there are bad words (starts)', function () { + bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { + return 'example text startsWith starts text startsAgain' + ' word'.repeat(994); + }); + expect(spec.interpretResponse(serverResponse, request).length).to.equal(0); + }); + + it('should return empty array when there are bad words (starts cyrillic)', function () { + bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { + return 'example text стартТекст старт text стартТекст' + ' дума'.repeat(994); + }); + expect(spec.interpretResponse(serverResponse, request).length).to.equal(0); + }); + + it('should return empty array when there are bad words (ends)', function () { + bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { + return 'example text wordEnds ends text anotherends' + ' word'.repeat(994); + }); + expect(spec.interpretResponse(serverResponse, request).length).to.equal(0); + }); + + it('should return empty array when there are bad words (ends cyrillic)', function () { + bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { + return 'example text ДругКрай край text ощеединкрай' + ' дума'.repeat(994); + }); + expect(spec.interpretResponse(serverResponse, request).length).to.equal(0); + }); + + it('should return empty array when there are bad words (regexp)', function () { + bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { + return 'example text xxxayyy zzxxxAyyyzz text xxxbyyy' + ' word'.repeat(994); }); expect(spec.interpretResponse(serverResponse, request).length).to.equal(0); }); it('should return non-empty array when there are not enough bad words (full)', function () { bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { - return 'example text badWord1 badWord1 example text' + ' word'.repeat(494); + return 'example text badword badword example text' + ' word'.repeat(994); }); expect(spec.interpretResponse(serverResponse, request).length).to.equal(1); }); it('should return non-empty array when there are not enough bad words (partial)', function () { bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { - return 'example text partialBadWord2 example' + ' word'.repeat(496); + return 'example text partialbadwordb example' + ' word'.repeat(996); }); expect(spec.interpretResponse(serverResponse, request).length).to.equal(1); }); it('should return non-empty array when there are no-bad word matches', function () { bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { - return 'example text partialBadWord1 example text' + ' word'.repeat(495); + return 'example text partialbadword example text' + ' word'.repeat(995); }); expect(spec.interpretResponse(serverResponse, request).length).to.equal(1); }); it('should return non-empty array when there are bad words and good words', function () { bodyStub = sinon.stub(window.top.document.body, 'innerText').get(function() { - return 'example text badWord1 badWord1 example badWord1 goodWord goodWord ' + ' word'.repeat(492); + return 'example text badword badword example badword goodWord goodWord ' + ' word'.repeat(992); }); expect(spec.interpretResponse(serverResponse, request).length).to.equal(1); }); From 6236e3f3b068c6ba3920115dd4a20df5e1385ebd Mon Sep 17 00:00:00 2001 From: Carlos Felix Date: Thu, 8 Dec 2022 08:09:35 -0600 Subject: [PATCH 509/569] Add source and version parameters to the 33across ID request (#9319) --- modules/33acrossIdSystem.js | 3 +++ test/spec/modules/33acrossIdSystem_spec.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/33acrossIdSystem.js b/modules/33acrossIdSystem.js index 3763fee5124..5e07fe9523d 100644 --- a/modules/33acrossIdSystem.js +++ b/modules/33acrossIdSystem.js @@ -13,6 +13,7 @@ import { uspDataHandler } from '../src/adapterManager.js'; const MODULE_NAME = '33acrossId'; const API_URL = 'https://lexicon.33across.com/v1/envelope'; const AJAX_TIMEOUT = 10000; +const CALLER_NAME = 'pbjs'; function getEnvelope(response) { if (!response.succeeded) { @@ -36,6 +37,8 @@ function calculateQueryStringParams(pid, gdprConsentData) { const params = { pid, gdpr: Number(gdprApplies), + src: CALLER_NAME, + ver: '$prebid.version$' }; if (uspString) { diff --git a/test/spec/modules/33acrossIdSystem_spec.js b/test/spec/modules/33acrossIdSystem_spec.js index 8198bd75381..765b320f925 100644 --- a/test/spec/modules/33acrossIdSystem_spec.js +++ b/test/spec/modules/33acrossIdSystem_spec.js @@ -43,7 +43,10 @@ describe('33acrossIdSystem', () => { expect(request.method).to.equal('GET'); expect(request.withCredentials).to.be.true; - expect(request.url).to.contain('https://lexicon.33across.com/v1/envelope?pid=12345'); + + const regExp = new RegExp('https://lexicon.33across.com/v1/envelope\\?pid=12345&gdpr=\\d&src=pbjs&ver=$prebid.version$'); + + expect(request.url).to.match(regExp); expect(completeCallback.calledOnceWithExactly('foo')).to.be.true; }); From 1b4d83c112e157a3ed1702e5f068b85cf8cce401 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 8 Dec 2022 07:32:14 -0700 Subject: [PATCH 510/569] Prebid Core: ORTB 2.5 translation utilities (#9263) * ORTB 2.5 spec definition * ORTB 2.5 translation * Only test translation of native reqs if FEATURES.NATIVE is set --- libraries/ortb2.5StrictTranslator/dsl.js | 54 +++ libraries/ortb2.5StrictTranslator/spec.js | 81 ++++ .../ortb2.5StrictTranslator/translator.js | 37 ++ libraries/ortb2.5Translator/translator.js | 82 ++++ test/spec/ortb2.5StrictTranslator/dsl_spec.js | 137 +++++++ .../spec/ortb2.5StrictTranslator/spec_spec.js | 358 ++++++++++++++++++ .../translator_spec.js | 16 + .../spec/ortb2.5Translator/translator_spec.js | 64 ++++ 8 files changed, 829 insertions(+) create mode 100644 libraries/ortb2.5StrictTranslator/dsl.js create mode 100644 libraries/ortb2.5StrictTranslator/spec.js create mode 100644 libraries/ortb2.5StrictTranslator/translator.js create mode 100644 libraries/ortb2.5Translator/translator.js create mode 100644 test/spec/ortb2.5StrictTranslator/dsl_spec.js create mode 100644 test/spec/ortb2.5StrictTranslator/spec_spec.js create mode 100644 test/spec/ortb2.5StrictTranslator/translator_spec.js create mode 100644 test/spec/ortb2.5Translator/translator_spec.js diff --git a/libraries/ortb2.5StrictTranslator/dsl.js b/libraries/ortb2.5StrictTranslator/dsl.js new file mode 100644 index 00000000000..8f9fc79feeb --- /dev/null +++ b/libraries/ortb2.5StrictTranslator/dsl.js @@ -0,0 +1,54 @@ +export const ERR_TYPE = 0; // field has wrong type (only objects, enums, and arrays of objects or enums are checked) +export const ERR_UNKNOWN_FIELD = 1; // field is not defined in ORTB 2.5 spec +export const ERR_ENUM = 2; // field is an enum and its value is not one of those listed in the ORTB 2.5 spec + +// eslint-disable-next-line symbol-description +export const extend = Symbol(); + +export function Obj(primitiveFields, spec = {}) { + const scan = (path, parent, field, value, onError) => { + if (value == null || typeof value !== 'object') { + onError(ERR_TYPE, path, parent, field, value); + return; + } + Object.entries(value).forEach(([k, v]) => { + if (v == null) return; + const kpath = path == null ? k : `${path}.${k}`; + if (spec.hasOwnProperty(k)) { + spec[k](kpath, value, k, v, onError); + return; + } + if (k !== 'ext' && !primitiveFields.includes(k)) { + onError(ERR_UNKNOWN_FIELD, kpath, value, k, v); + } + }); + }; + scan[extend] = (extraPrimitives, specOverride = {}) => + Obj(primitiveFields.concat(extraPrimitives), Object.assign({}, spec, specOverride)); + return scan; +} + +export const ID = Obj(['id']); +export const Named = ID[extend](['name']); + +export function Arr(def) { + return (path, parent, field, value, onError) => { + if (!Array.isArray(value)) { + onError(ERR_TYPE, path, parent, field, value); + } else { + value.forEach((item, i) => def(`${path}.${i}`, value, i, item, onError)); + } + }; +} + +export function IntEnum(min, max) { + return (path, parent, field, value, onError) => { + const errno = (() => { + if (typeof value !== 'number') return ERR_TYPE; + if (isNaN(value) || value > max || value < min) return ERR_ENUM; + })(); + if (errno != null) { + onError(errno, path, parent, field, value); + } + }; +} diff --git a/libraries/ortb2.5StrictTranslator/spec.js b/libraries/ortb2.5StrictTranslator/spec.js new file mode 100644 index 00000000000..0ffb17a2e72 --- /dev/null +++ b/libraries/ortb2.5StrictTranslator/spec.js @@ -0,0 +1,81 @@ +import {Arr, extend, ID, IntEnum, Named, Obj} from './dsl.js'; + +const CatDomain = Named[extend](['cat', 'domain']); +const Segment = Named[extend](['value']); +const Data = Named[extend]([], { + segment: Arr(Segment) +}); +const Content = ID[extend](['episode', 'title', 'series', 'season', 'artist', 'genre', 'album', 'isrc', 'url', 'cat', 'contentrating', 'userrating', 'keywords', 'livestream', 'sourcerelationship', 'len', 'language', 'embeddable'], { + producer: CatDomain, + data: Arr(Data), + prodq: IntEnum(0, 3), + videoquality: IntEnum(0, 3), + context: IntEnum(1, 7), + qagmediarating: IntEnum(1, 3), +}); + +const Client = CatDomain[extend](['sectioncat', 'pagecat', 'privacypolicy', 'keywords'], { + publisher: CatDomain, content: Content, +}); +const Site = Client[extend](['page', 'ref', 'search', 'mobile']); +const App = Client[extend](['bundle', 'storeurl', 'ver', 'paid']); + +const Geo = Obj(['lat', 'lon', 'accuracy', 'lastfix', 'country', 'region', 'regionfips104', 'metro', 'city', 'zip', 'utcoffset'], { + type: IntEnum(1, 3), + ipservice: IntEnum(1, 4) +}); +const Device = Obj(['ua', 'dnt', 'lmt', 'ip', 'ipv6', 'make', 'model', 'os', 'osv', 'hwv', 'h', 'w', 'ppi', 'pxratio', 'js', 'geofetch', 'flashver', 'language', 'carrier', 'mccmnc', 'ifa', 'didsha1', 'didmd5', 'dpidsha1', 'dpidmd5', 'macsha1', 'macmd5'], { + geo: Geo, devicetype: IntEnum(1, 7), connectiontype: IntEnum(0, 6) +}); +const User = ID[extend](['buyeruid', 'yob', 'gender', 'keywords', 'customdata'], { + geo: Geo, data: Arr(Data), +}); + +const Floorable = ID[extend](['bidfloor', 'bidfloorcur']); +const Deal = Floorable[extend](['at', 'wseat', 'wadomain']); +const Pmp = Obj(['private_auction'], { + deals: Arr(Deal), +}); +const Format = Obj(['w', 'h', 'wratio', 'hratio', 'wmin']); +const MediaType = Obj(['mimes'], { + api: Arr(IntEnum(1, 6)), battr: Arr(IntEnum(1, 17)) +}); +const Banner = MediaType[extend](['id', 'w', 'h', 'wmax', 'hmax', 'hmin', 'wmin', 'topframe', 'vcm'], { + format: Arr(Format), btype: Arr(IntEnum(1, 4)), pos: IntEnum(0, 7), expdir: Arr(IntEnum(1, 5)) +}); +const Native = MediaType[extend](['request', 'ver']); +const RichMediaType = MediaType[extend](['minduration', 'maxduration', 'startdelay', 'sequence', 'maxextended', 'minbitrate', 'maxbitrate'], { + protocols: Arr(IntEnum(1, 10)), + delivery: Arr(IntEnum(1, 3)), + companionad: Arr(Banner), + companiontype: Arr(IntEnum(1, 3)), +}); +/* +const Audio = RichMediaType[extend](['maxseq', 'stitched'], { + feed: IntEnum(1, 3), nvol: IntEnum(0, 4), +}); + */ +const Video = RichMediaType[extend](['w', 'h', 'skip', 'skipmin', 'skipafter', 'boxingallowed'], { + pos: IntEnum(0, 7), + protocol: IntEnum(1, 10), + placement: IntEnum(1, 5), + linearity: IntEnum(1, 2), + playbackmethod: Arr(IntEnum(1, 6)), + playbackend: IntEnum(1, 3), +}); +const Metric = Obj(['type', 'value', 'vendor']); +const Imp = (() => { + const spec = { + metric: Arr(Metric), banner: Banner, video: Video, pmp: Pmp, + }; + if (FEATURES.NATIVE) { + spec.native = Native; + } + return Floorable[extend](['displaymanager', 'displaymanagerver', 'instl', 'tagid', 'clickbrowser', 'secure', 'iframebuster', 'exp'], spec); +})(); + +const Regs = Obj(['coppa']); +const Source = Obj(['fd', 'tid', 'pchain']); +export const BidRequest = ID[extend](['test', 'at', 'tmax', 'wseat', 'bseat', 'allimps', 'cur', 'wlang', 'bcat', 'badv', 'bapp'], { + imp: Arr(Imp), site: Site, app: App, device: Device, user: User, source: Source, regs: Regs +}); diff --git a/libraries/ortb2.5StrictTranslator/translator.js b/libraries/ortb2.5StrictTranslator/translator.js new file mode 100644 index 00000000000..c6f651e2476 --- /dev/null +++ b/libraries/ortb2.5StrictTranslator/translator.js @@ -0,0 +1,37 @@ +import {BidRequest} from './spec.js'; +import {logWarn} from '../../src/utils.js'; +import {toOrtb25} from '../ortb2.5Translator/translator.js'; + +function deleteField(errno, path, obj, field, value) { + logWarn(`${path} is not valid ORTB 2.5, field will be removed from request:`, value); + Array.isArray(obj) ? obj.splice(field, 1) : delete obj[field]; +} + +/** + * Translates an ortb request to 2.5, and removes from the result any field that is: + * - not defined in the 2.5 spec, or + * - defined as an enum, but has a value that is not listed in the 2.5 spec. + * + * `ortb2` is modified in place and returned. + * + * Note that using this utility will cause your adapter to pull in an additional ~3KB after minification. + * If possible, consider making your endpoint tolerant to unrecognized or invalid fields instead. + * + * + * @param ortb2 ORTB request + * @param translator translation function. The default moves 2.x fields that have a known standard location in 2.5. + * See the `ortb2.5Translator` library. + * @param onError a function invoked once for each field that is not valid according to the 2.5 spec; it takes + * (errno, path, obj, field, value), where: + * - errno is an error code (defined in dsl.js) + * - path is the JSON path of the offending field, for example `regs.gdpr` + * - obj is the object containing the offending field, for example `ortb2.regs` + * - field is the field name, for example `'gdpr'` + * - value is `obj[field]`. + * The default logs a warning and deletes the offending field. + */ +export function toOrtb25Strict(ortb2, translator = toOrtb25, onError = deleteField) { + ortb2 = translator(ortb2); + BidRequest(null, null, null, ortb2, onError); + return ortb2; +} diff --git a/libraries/ortb2.5Translator/translator.js b/libraries/ortb2.5Translator/translator.js new file mode 100644 index 00000000000..1afad516ef0 --- /dev/null +++ b/libraries/ortb2.5Translator/translator.js @@ -0,0 +1,82 @@ +import {deepAccess, deepSetValue, logError} from '../../src/utils.js'; + +export const EXT_PROMOTIONS = [ + 'source.schain', + 'regs.gdpr', + 'regs.us_privacy', + 'regs.gpp', + 'user.consent', + 'user.eids' +]; + +export function splitPath(path) { + const parts = path.split('.'); + const prefix = parts.slice(0, parts.length - 1).join('.'); + const field = parts[parts.length - 1]; + return [prefix, field]; +} + +/** + * @param sourcePath a JSON path such as `regs.us_privacy` + * @param dest {function(String, String): String} a function taking (prefix, field) and returning a destination path; + * for example, ('regs', 'us_privacy') => 'regs.ext.us_privacy' + * @return {(function({}): (function(): void|undefined))|*} a function that takes an object and, if it contains + * sourcePath, copies its contents to destinationPath, returning a function that deletes the original sourcePath. + */ +export function moveRule(sourcePath, dest = (prefix, field) => `${prefix}.ext.${field}`) { + const [prefix, field] = splitPath(sourcePath); + dest = dest(prefix, field); + return (ortb2) => { + const obj = deepAccess(ortb2, prefix); + if (obj?.[field] != null) { + deepSetValue(ortb2, dest, obj[field]); + return () => delete obj[field]; + } + }; +} + +function kwarrayRule(section) { + // move 2.6 `kwarray` into 2.5 comma-separated `keywords`. + return (ortb2) => { + const kwarray = ortb2[section]?.kwarray; + if (kwarray != null) { + let kw = (ortb2[section].keywords || '').split(','); + if (Array.isArray(kwarray)) kw.push(...kwarray); + ortb2[section].keywords = kw.join(','); + return () => delete ortb2[section].kwarray; + } + }; +} + +export const DEFAULT_RULES = Object.freeze([ + ...EXT_PROMOTIONS.map((f) => moveRule(f)), + ...['app', 'content', 'site', 'user'].map(kwarrayRule) +]); + +/** + * Factory for ORTB 2.5 translation functions. + * + * @param deleteFields if true, the translation function will remove fields that have been translated (transferred somewhere else within the request) + * @param rules translation rules; an array of functions of the type returned by `moveRule` + * @return {function({}): {}} a translation function that takes an ORTB object, modifies it in place, and returns it. + */ +export function ortb25Translator(deleteFields = true, rules = DEFAULT_RULES) { + return function (ortb2) { + rules.forEach(f => { + try { + const deleter = f(ortb2); + if (typeof deleter === 'function' && deleteFields) deleter(); + } catch (e) { + logError('Error translating request to ORTB 2.5', e); + } + }) + return ortb2; + } +} + +/** + * Translate an ortb request to version 2.5 by moving 2.6 (and later) fields that have a standardized 2.5 extension. + * + * The request is modified in place and returned. + */ +export const toOrtb25 = ortb25Translator(); diff --git a/test/spec/ortb2.5StrictTranslator/dsl_spec.js b/test/spec/ortb2.5StrictTranslator/dsl_spec.js new file mode 100644 index 00000000000..c9b4575bcd2 --- /dev/null +++ b/test/spec/ortb2.5StrictTranslator/dsl_spec.js @@ -0,0 +1,137 @@ +import {Arr, ERR_ENUM, ERR_TYPE, ERR_UNKNOWN_FIELD, IntEnum, Obj} from '../../../libraries/ortb2.5StrictTranslator/dsl.js'; +import {deepClone} from '../../../src/utils.js'; + +describe('DSL', () => { + const spec = (() => { + const inner = Obj(['p21', 'p22'], { + enum: IntEnum(10, 20), + enumArray: Arr(IntEnum(10, 20)) + }); + return Obj(['p11', 'p12'], { + inner, + innerArray: Arr(inner) + }); + })(); + + let onError; + + function scan(obj) { + spec(null, null, null, obj, onError); + } + + beforeEach(() => { + onError = sinon.stub(); + }); + + it('checks object type', () => { + scan(null); + sinon.assert.calledWith(onError, ERR_TYPE, null, null, null, null); + }); + it('ignores known fields and ext', () => { + scan({p11: 1, p12: 2, ext: {e1: 1, e2: 2}}); + sinon.assert.notCalled(onError); + }); + it('detects unknown fields', () => { + const obj = {p11: 1, unk: 2}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_UNKNOWN_FIELD, 'unk', obj, 'unk', 2); + }); + describe('when nested', () => { + describe('directly', () => { + it('detects unknown fields', () => { + const obj = {inner: {p21: 1, unk: 2}}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_UNKNOWN_FIELD, 'inner.unk', obj.inner, 'unk', 2); + }); + it('accepts enum values in range', () => { + scan({inner: {enum: 12}}); + sinon.assert.notCalled(onError); + }); + [Infinity, NaN, -Infinity].forEach(val => { + it(`does not accept ${val} in enum`, () => { + const obj = {inner: {enum: val}}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_ENUM, 'inner.enum', obj.inner, 'enum', val); + }); + }); + it('accepts arrays of enums that are in range', () => { + scan({inner: {enumArray: [12, 13]}}); + sinon.assert.notCalled(onError); + }) + it('detects enum values out of range', () => { + const obj = {inner: {enum: -1}}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_ENUM, 'inner.enum', obj.inner, 'enum', -1); + }); + it('detects enum values that are not numbers', () => { + const obj = {inner: {enum: 'err'}}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_TYPE, 'inner.enum', obj.inner, 'enum', 'err'); + }) + it('detects arrays of enums that are out of range', () => { + const obj = {inner: {enumArray: [12, 13, -1, 14]}}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_ENUM, 'inner.enumArray.2', obj.inner.enumArray, 2, -1); + }); + it('detects when enum arrays are not arrays', () => { + const obj = {inner: {enumArray: 'err'}}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_TYPE, 'inner.enumArray', obj.inner, 'enumArray', 'err'); + }); + it('detects items within enum arrays that are not numbers', () => { + const obj = {inner: {enumArray: [12, 'err', 13]}}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_TYPE, 'inner.enumArray.1', obj.inner.enumArray, 1, 'err'); + }) + }); + describe('into arrays', () => { + it('detects if inner array is not an array', () => { + const obj = {innerArray: 'err', inner: {p21: 1}}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_TYPE, 'innerArray', obj, 'innerArray', 'err'); + }); + it('detects when elements of inner array are not objects', () => { + const obj = {innerArray: [{p21: 1}, 'err', {ext: {r: 1}}]}; + scan(obj); + sinon.assert.calledOnce(onError); + sinon.assert.calledWith(onError, ERR_TYPE, 'innerArray.1', obj.innerArray, 1, 'err'); + }); + const oos = { + innerArray: [ + {p22: 2, unk: 3, enumArray: [-1, 12, 'err']}, + {p21: 1, enum: -1, ext: {e: 1}}, + ] + }; + it('detects invalid properties within inner array', () => { + const obj = deepClone(oos); + scan(obj); + sinon.assert.calledWith(onError, ERR_UNKNOWN_FIELD, 'innerArray.0.unk', obj.innerArray[0], 'unk', 3); + sinon.assert.calledWith(onError, ERR_ENUM, 'innerArray.0.enumArray.0', obj.innerArray[0].enumArray, 0, -1); + sinon.assert.calledWith(onError, ERR_TYPE, 'innerArray.0.enumArray.2', obj.innerArray[0].enumArray, 2, 'err'); + sinon.assert.calledWith(onError, ERR_ENUM, 'innerArray.1.enum', obj.innerArray[1], 'enum', -1); + }); + it('can remove all invalid properties during scan', () => { + onError.callsFake((errno, path, obj, field) => { + Array.isArray(obj) ? obj.splice(field, 1) : delete obj[field]; + }); + const obj = deepClone(oos); + scan(obj); + expect(obj).to.eql({ + innerArray: [ + {p22: 2, enumArray: [12]}, + {p21: 1, ext: {e: 1}} + ] + }); + }) + }) + }) +}); diff --git a/test/spec/ortb2.5StrictTranslator/spec_spec.js b/test/spec/ortb2.5StrictTranslator/spec_spec.js new file mode 100644 index 00000000000..a54b551bf61 --- /dev/null +++ b/test/spec/ortb2.5StrictTranslator/spec_spec.js @@ -0,0 +1,358 @@ +import {BidRequest} from '../../../libraries/ortb2.5StrictTranslator/spec.js'; + +// sample requests taken from ORTB 2.5 spec: https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf +const SAMPLE_REQUESTS = [ + { + 'id': '80ce30c53c16e6ede735f123ef6e32361bfc7b22', + 'at': 1, + 'cur': ['USD'], + 'imp': [ + { + 'id': '1', + 'bidfloor': 0.03, + 'banner': { + 'h': 250, 'w': 300, 'pos': 0 + } + } + ], + 'site': { + 'id': '102855', + 'cat': ['IAB3-1'], + 'domain': 'www.foobar.com', + 'page': 'http://www.foobar.com/1234.html ', + 'publisher': { + 'id': '8953', + 'name': 'foobar.com', + 'cat': ['IAB3-1'], + 'domain': 'foobar.com' + } + }, + 'device': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2', + 'ip': '123.145.167.10' + }, + 'user': { + 'id': '55816b39711f9b5acf3b90e313ed29e51665623f' + } + }, + { + 'id': '123456789316e6ede735f123ef6e32361bfc7b22', + 'at': 2, + 'cur': ['USD'], + 'imp': [ + { + 'id': '1', + 'bidfloor': 0.03, + 'iframebuster': ['vendor1.com', 'vendor2.com'], + 'banner': { + 'h': 250, + 'w': 300, + 'pos': 0, + 'battr': [13], + 'expdir': [2, 4] + } + } + ], + 'site': { + 'id': '102855', + 'cat': ['IAB3-1'], + 'domain': 'www.foobar.com', + 'page': 'http://www.foobar.com/1234.html', + 'publisher': { + 'id': '8953', + 'name': 'foobar.com', + 'cat': ['IAB3-1'], + 'domain': 'foobar.com' + } + }, + 'device': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2', + 'ip': '123.145.167.10' + }, + 'user': { + 'id': '55816b39711f9b5acf3b90e313ed29e51665623f', + 'buyeruid': '545678765467876567898765678987654', + 'data': [ + { + 'id': '6', + 'name': 'Data Provider 1', + 'segment': [ + { + 'id': '12341318394918', 'name': 'auto intenders' + }, + { + 'id': '1234131839491234', 'name': 'auto enthusiasts' + }, + { + 'id': '23423424', + 'name': 'data-provider1-age', + 'value': '30-40' + } + ] + } + ] + } + }, + { + 'id': 'IxexyLDIIk', + 'at': 2, + 'bcat': ['IAB25', 'IAB7-39', 'IAB8-18', 'IAB8-5', 'IAB9-9'], + 'badv': ['apple.com', 'go-text.me', 'heywire.com'], + 'imp': [ + { + 'id': '1', + 'bidfloor': 0.5, + 'instl': 0, + 'tagid': 'agltb3B1Yi1pbmNyDQsSBFNpdGUY7fD0FAw', + 'banner': { + 'w': 728, + 'h': 90, + 'pos': 1, + 'btype': [4], + 'battr': [14], + 'api': [3] + } + } + ], + 'app': { + 'id': 'agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA', + 'name': 'Yahoo Weather', + 'cat': ['IAB15', 'IAB15-10'], + 'ver': '1.0.2', + 'bundle': '12345', + 'storeurl': 'https://itunes.apple.com/id628677149', + 'publisher': { + 'id': 'agltb3B1Yi1pbmNyDAsSA0FwcBiJkfTUCV', + 'name': 'yahoo', + 'domain': 'www.yahoo.com' + } + }, + 'device': { + 'dnt': 0, + 'ua': 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3', + 'ip': '123.145.167.189', + 'ifa': 'AA000DFE74168477C70D291f574D344790E0BB11', + 'carrier': 'VERIZON', + 'language': 'en', + 'make': 'Apple', + 'model': 'iPhone', + 'os': 'iOS', + 'osv': '6.1', + 'js': 1, + 'connectiontype': 3, + 'devicetype': 1, + 'geo': { + 'lat': 35.012345, + 'lon': -115.12345, + 'country': 'USA', + 'metro': '803', + 'region': 'CA', + 'city': 'Los Angeles', + 'zip': '90049' + } + }, + 'user': { + 'id': 'ffffffd5135596709273b3a1a07e466ea2bf4fff', + 'yob': 1984, + 'gender': 'M' + } + }, + { + 'id': '1234567893', + 'at': 2, + 'tmax': 120, + 'imp': [ + { + 'id': '1', + 'bidfloor': 0.03, + 'video': { + 'w': 640, + 'h': 480, + 'pos': 1, + 'startdelay': 0, + 'minduration': 5, + 'maxduration': 30, + 'maxextended': 30, + 'minbitrate': 300, + 'maxbitrate': 1500, + 'api': [1, 2], + 'protocols': [2, 3], + 'mimes': [ + 'video/x-flv', + 'video/mp4', + 'application/x-shockwave-flash', + 'application/javascript' + ], + 'linearity': 1, + 'boxingallowed': 1, + 'playbackmethod': [1, 3], + 'delivery': [2], + 'battr': [13, 14], + 'companionad': [ + { + 'id': '1234567893-1', + 'w': 300, + 'h': 250, + 'pos': 1, + 'battr': [13, 14], + 'expdir': [2, 4] + }, + { + 'id': '1234567893-2', + 'w': 728, + 'h': 90, + 'pos': 1, + 'battr': [13, 14] + } + ], + 'companiontype': [1, 2] + } + } + ], + 'site': { + 'id': '1345135123', + 'name': 'Site ABCD', + 'domain': 'siteabcd.com', + 'cat': ['IAB2-1', 'IAB2-2'], + 'page': 'http://siteabcd.com/page.htm', + 'ref': 'http://referringsite.com/referringpage.htm', + 'privacypolicy': 1, + 'publisher': { + 'id': 'pub12345', 'name': 'Publisher A' + }, + 'content': { + 'id': '1234567', + 'series': 'All About Cars', + 'season': '2', + 'episode': 23, + 'title': 'Car Show', + 'cat': ['IAB2-2'], + 'keywords': 'keyword-a,keyword-b,keyword-c' + } + }, + 'device': { + 'ip': '64.124.253.1', + 'ua': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16', + 'os': 'OS X', + 'flashver': '10.1', + 'js': 1 + }, + 'user': { + 'id': '456789876567897654678987656789', + 'buyeruid': '545678765467876567898765678987654', + 'data': [ + { + 'id': '6', + 'name': 'Data Provider 1', + 'segment': [ + { + 'id': '12341318394918', 'name': 'auto intenders' + }, + { + 'id': '1234131839491234', 'name': 'auto enthusiasts' + } + ] + } + ] + } + }, + { + 'id': '80ce30c53c16e6ede735f123ef6e32361bfc7b22', + 'at': 1, + 'cur': ['USD'], + 'imp': [ + { + 'id': '1', + 'bidfloor': 0.03, + 'banner': { + 'h': 250, 'w': 300, 'pos': 0 + }, + 'pmp': { + 'private_auction': 1, + 'deals': [ + { + 'id': 'AB-Agency1-0001', + 'at': 1, + 'bidfloor': 2.5, + 'wseat': ['Agency1'] + }, + { + 'id': 'XY-Agency2-0001', + 'at': 2, + 'bidfloor': 2, + 'wseat': ['Agency2'] + } + ] + } + } + ], + 'site': { + 'id': '102855', + 'domain': 'www.foobar.com', + 'cat': ['IAB3-1'], + 'page': 'http://www.foobar.com/1234.html', + 'publisher': { + 'id': '8953', + 'name': 'foobar.com', + 'cat': ['IAB3-1'], + 'domain': 'foobar.com' + } + }, + 'device': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2', + 'ip': '123.145.167.10' + }, + 'user': { + 'id': '55816b39711f9b5acf3b90e313ed29e51665623f' + } + }, +]; + +if (FEATURES.NATIVE) { + SAMPLE_REQUESTS.push({ + 'id': '80ce30c53c16e6ede735f123ef6e32361bfc7b22', + 'at': 1, + 'cur': ['USD'], + 'imp': [ + { + 'id': '1', + 'bidfloor': 0.03, + 'native': { + 'request': '{"native":{"ver":"1.0","assets":[ ... ]}}', + 'ver': '1.0', + 'api': [3], + 'battr': [13, 14] + } + } + ], + 'site': { + 'id': '102855', + 'cat': ['IAB3-1'], + 'domain': 'www.foobar.com', + 'page': 'http://www.foobar.com/1234.html ', + 'publisher': { + 'id': '8953', + 'name': 'foobar.com', + 'cat': ['IAB3-1'], + 'domain': 'foobar.com' + } + }, + 'device': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2', + 'ip': '123.145.167.10' + }, + 'user': { + 'id': '55816b39711f9b5acf3b90e313ed29e51665623f' + } + }); +} + +describe('BidRequest spec', () => { + SAMPLE_REQUESTS.forEach((req, i) => { + it(`accepts sample #${i}`, () => { + const onError = sinon.stub(); + BidRequest(null, null, null, req, onError); + sinon.assert.notCalled(onError); + }); + }); +}); diff --git a/test/spec/ortb2.5StrictTranslator/translator_spec.js b/test/spec/ortb2.5StrictTranslator/translator_spec.js new file mode 100644 index 00000000000..4bda3d96235 --- /dev/null +++ b/test/spec/ortb2.5StrictTranslator/translator_spec.js @@ -0,0 +1,16 @@ +import {toOrtb25Strict} from '../../../libraries/ortb2.5StrictTranslator/translator.js'; + +describe('toOrtb25Strict', () => { + let translator; + beforeEach(() => { + translator = sinon.stub().callsFake((o) => o); + }) + it('uses provided translator', () => { + translator.reset(); + translator.callsFake(() => ({id: 'test'})); + expect(toOrtb25Strict(null, translator)).to.eql({id: 'test'}); + }); + it('removes fields out of spec', () => { + expect(toOrtb25Strict({unk: 'field', imp: ['err', {}]}, translator)).to.eql({imp: [{}]}); + }); +}); diff --git a/test/spec/ortb2.5Translator/translator_spec.js b/test/spec/ortb2.5Translator/translator_spec.js new file mode 100644 index 00000000000..db20a8f59be --- /dev/null +++ b/test/spec/ortb2.5Translator/translator_spec.js @@ -0,0 +1,64 @@ +import {EXT_PROMOTIONS, moveRule, splitPath, toOrtb25} from '../../../libraries/ortb2.5Translator/translator.js'; +import {deepAccess, deepClone, deepSetValue} from '../../../src/utils.js'; + +describe('ORTB 2.5 translation', () => { + describe('moveRule', () => { + const rule = moveRule('f1.f2.f3', (prefix, field) => `${prefix}.m1.m2.${field}`); + + function applyRule(rule, obj, del = true) { + obj = deepClone(obj); + const deleter = rule(obj); + if (typeof deleter === 'function' && del) { + deleter(); + } + return obj; + } + + it('returns undef when field is not present', () => { + expect(rule({})).to.eql(undefined); + }); + it('can copy field', () => { + expect(applyRule(rule, {f1: {f2: {f3: 'value'}}}, false)).to.eql({ + f1: { + f2: { + f3: 'value', + m1: {m2: {f3: 'value'}} + } + } + }); + }); + it('can move field', () => { + expect(applyRule(rule, {f1: {f2: {f3: 'value'}}}, true)).to.eql({f1: {f2: {m1: {m2: {f3: 'value'}}}}}); + }); + }); + describe('toOrtb25', () => { + EXT_PROMOTIONS.forEach(path => { + const newPath = (() => { + const [prefix, field] = splitPath(path); + return `${prefix}.ext.${field}`; + })(); + + it(`moves ${path} to ${newPath}`, () => { + const obj = {}; + deepSetValue(obj, path, 'val'); + toOrtb25(obj); + expect(deepAccess(obj, path)).to.eql(undefined); + expect(deepAccess(obj, newPath)).to.eql('val'); + }); + }); + it('moves kwarray into keywords', () => { + expect(toOrtb25({app: {keywords: 'k1,k2', kwarray: ['ka1', 'ka2']}})).to.eql({app: {keywords: 'k1,k2,ka1,ka2'}}); + }); + it('does not choke if kwarray is not an array', () => { + expect(toOrtb25({site: {keywords: 'k1,k2', kwarray: 'err'}})).to.eql({site: {keywords: 'k1,k2'}}); + }); + it('does not choke if keywords is not a string', () => { + expect(toOrtb25({user: {keywords: {}, kwarray: ['ka1', 'ka2']}})).to.eql({ + user: { + keywords: {}, + kwarray: ['ka1', 'ka2'] + } + }); + }); + }); +}); From 4346aa82264a08c7f135fd2de4acb06285c1868e Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Thu, 8 Dec 2022 09:25:07 -0800 Subject: [PATCH 511/569] Prebid Core: Addition of Optional Category Targeting Key (#9268) * addition of category optional targeting * removed console log statements * console.log statements for debugging * updated tests * formatting changes * added pbs test --- src/auction.js | 12 ++++++++++- src/constants.json | 3 ++- test/spec/auctionmanager_spec.js | 34 +++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/auction.js b/src/auction.js index 563eb85e788..87397b0dc15 100644 --- a/src/auction.js +++ b/src/auction.js @@ -828,6 +828,16 @@ export const getAdvertiserDomain = () => { } } +/** + * This function returns a function to get the primary category id from bid response meta + * @returns {function} + */ +export const getPrimaryCatId = () => { + return (bid) => { + return (bid.meta && bid.meta.primaryCatId) ? bid.meta.primaryCatId : ''; + } +} + // factory for key value objs function createKeyVal(key, value) { return { @@ -853,6 +863,7 @@ function defaultAdserverTargeting() { createKeyVal(TARGETING_KEYS.SOURCE, 'source'), createKeyVal(TARGETING_KEYS.FORMAT, 'mediaType'), createKeyVal(TARGETING_KEYS.ADOMAIN, getAdvertiserDomain()), + createKeyVal(TARGETING_KEYS.ACAT, getPrimaryCatId()), ] } @@ -865,7 +876,6 @@ function defaultAdserverTargeting() { export function getStandardBidderSettings(mediaType, bidderCode) { const TARGETING_KEYS = CONSTANTS.TARGETING_KEYS; const standardSettings = Object.assign({}, bidderSettings.settingsFor(null)); - if (!standardSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]) { standardSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING] = defaultAdserverTargeting(); } diff --git a/src/constants.json b/src/constants.json index 30ade5fccbe..e33d65f3fb1 100644 --- a/src/constants.json +++ b/src/constants.json @@ -76,7 +76,8 @@ "UUID": "hb_uuid", "CACHE_ID": "hb_cache_id", "CACHE_HOST": "hb_cache_host", - "ADOMAIN": "hb_adomain" + "ADOMAIN": "hb_adomain", + "ACAT": "hb_acat" }, "DEFAULT_TARGETING_KEYS": { "BIDDER": "hb_bidder", diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 46350895050..e1ecf801aa3 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -186,7 +186,8 @@ describe('auctionmanager.js', function () { source: 'client', mediaType: 'banner', meta: { - advertiserDomains: ['adomain'] + advertiserDomains: ['adomain'], + primaryCatId: 'IAB-test' } }; @@ -200,6 +201,7 @@ describe('auctionmanager.js', function () { expected[ CONSTANTS.TARGETING_KEYS.SOURCE ] = bid.source; expected[ CONSTANTS.TARGETING_KEYS.FORMAT ] = bid.mediaType; expected[ CONSTANTS.TARGETING_KEYS.ADOMAIN ] = bid.meta.advertiserDomains[0]; + expected[ CONSTANTS.TARGETING_KEYS.ACAT ] = bid.meta.primaryCatId; if (bid.mediaType === 'video') { expected[ CONSTANTS.TARGETING_KEYS.UUID ] = bid.videoCacheKey; expected[ CONSTANTS.TARGETING_KEYS.CACHE_ID ] = bid.videoCacheKey; @@ -290,6 +292,12 @@ describe('auctionmanager.js', function () { val: function (bidResponse) { return bidResponse.meta.advertiserDomains[0]; } + }, + { + key: CONSTANTS.TARGETING_KEYS.ACAT, + val: function (bidResponse) { + return bidResponse.meta.primaryCatId; + } } ] @@ -367,6 +375,12 @@ describe('auctionmanager.js', function () { val: function (bidResponse) { return bidResponse.meta.advertiserDomains[0]; } + }, + { + key: CONSTANTS.TARGETING_KEYS.ACAT, + val: function (bidResponse) { + return bidResponse.meta.primaryCatId; + } } ] @@ -455,6 +469,24 @@ describe('auctionmanager.js', function () { assert.deepEqual(response, expected); }); + it('Should set targeting as expecting when pbs is enabled', function () { + config.setConfig({ + s2sConfig: { + accountId: '1', + enabled: true, + defaultVendor: 'appnexus', + bidders: ['appnexus'], + timeout: 1000, + adapter: 'prebidServer' + } + }); + + $$PREBID_GLOBAL$$.bidderSettings = {}; + let expected = getDefaultExpected(bid); + let response = getKeyValueTargetingPairs(bid.bidderCode, bid); + assert.deepEqual(response, expected); + }); + it('Custom bidCpmAdjustment for one bidder and inherit standard but doesn\'t use standard bidCpmAdjustment', function () { $$PREBID_GLOBAL$$.bidderSettings = { From f5f276bb827d5b81b12b2c62e405bb11768690e5 Mon Sep 17 00:00:00 2001 From: ahmadlob <109217988+ahmadlob@users.noreply.github.com> Date: Thu, 8 Dec 2022 19:54:43 +0200 Subject: [PATCH 512/569] Taboola Bid Adapter: Fixing Accepting Bid Floor Mechanism (#9279) * use-convention-for-bidfloor-extraction * use-convention-for-bidfloor-extraction * add-unit-tests --- modules/taboolaBidAdapter.js | 27 ++++++++++---- test/spec/modules/taboolaBidAdapter_spec.js | 39 +++++++++++++++++++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 9bd42651796..670d28ab64e 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -170,15 +170,28 @@ function getSiteProperties({publisherId, bcat = []}, refererInfo) { function getImps(validBidRequests) { return validBidRequests.map((bid, id) => { - const {tagId, bidfloor = null, bidfloorcur = CURRENCY} = bid.params; - - return { + const {tagId} = bid.params; + const imp = { id: id + 1, banner: getBanners(bid), - tagid: tagId, - bidfloor, - bidfloorcur, - }; + tagid: tagId + } + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: CURRENCY, + mediaType: BANNER, + size: '*' + }); + if (typeof floorInfo === 'object' && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { + imp.bidfloor = parseFloat(floorInfo.floor); + imp.bidfloorcur = CURRENCY; + } + } else { + const {bidfloor = null, bidfloorcur = CURRENCY} = bid.params; + imp.bidfloor = bidfloor; + imp.bidfloorcur = bidfloorcur; + } + return imp; }); } diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index c0b16ac40fc..5bde75cd0b6 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -167,6 +167,45 @@ describe('Taboola Adapter', function () { expect(resData.imp[0].bidfloorcur).to.deep.equal('EUR'); }); + it('should pass bid floor', function () { + const bidRequest = { + ...defaultBidRequest, + params: {...commonBidRequest.params}, + getFloor: function() { + return { + currency: 'USD', + floor: 2.7, + } + } + }; + const res = spec.buildRequests([bidRequest], commonBidderRequest); + const resData = JSON.parse(res.data); + expect(resData.imp[0].bidfloor).to.deep.equal(2.7); + expect(resData.imp[0].bidfloorcur).to.deep.equal('USD'); + }); + + it('should pass bid floor even if they is a bid floor param', function () { + const optionalParams = { + bidfloor: 0.25, + bidfloorcur: 'EUR' + }; + + const bidRequest = { + ...defaultBidRequest, + params: {...commonBidRequest.params, ...optionalParams}, + getFloor: function() { + return { + currency: 'USD', + floor: 2.7, + } + } + }; + const res = spec.buildRequests([bidRequest], commonBidderRequest); + const resData = JSON.parse(res.data); + expect(resData.imp[0].bidfloor).to.deep.equal(2.7); + expect(resData.imp[0].bidfloorcur).to.deep.equal('USD'); + }); + it('should pass bidder timeout', function () { const bidderRequest = { ...commonBidderRequest, From 69a0f6a2be7a9d8eea7c3d0e742b9284eef0bba8 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 8 Dec 2022 19:04:28 +0000 Subject: [PATCH 513/569] Prebid 7.28.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f73e700aa42..d6484617a3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.28.0-pre", + "version": "7.28.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 7bbe18037b4..8d9b6a119df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.28.0-pre", + "version": "7.28.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From d4b449f19a3326a752b5d87df2ea9b05669bf1d5 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 8 Dec 2022 19:04:28 +0000 Subject: [PATCH 514/569] Increment version to 7.29.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6484617a3b..006b8bcdcab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.28.0", + "version": "7.29.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 8d9b6a119df..5ee4cb728b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.28.0", + "version": "7.29.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From bf5b15f4e532bc6a519185c8520b3368139c2b31 Mon Sep 17 00:00:00 2001 From: Bill Newman Date: Fri, 9 Dec 2022 12:58:53 +0200 Subject: [PATCH 515/569] Colossus Bid Adapter: update user sync (#9327) * add video&native traffic colossus ssp * Native obj validation * Native obj validation #2 * Added size field in requests * fixed test * fix merge conflicts * move to 3.0 * move to 3.0 * fix IE11 new URL issue * fix IE11 new URL issue * fix IE11 new URL issue * https for 3.0 * add https test * add ccp and schain features * fix test * sync with upstream, fix conflicts * Update colossussspBidAdapter.js remove commented code * Update colossussspBidAdapter.js lint fix * identity extensions * identity extensions * fix * fix * fix * fix * fix * add tests for user ids * fix * fix * fix * fix * fix * fix * fix * add gdpr support * add gdpr support * id5id support * Update colossussspBidAdapter.js add bidfloor parameter * Update colossussspBidAdapter.js check bidfloor * Update colossussspBidAdapter.js * Update colossussspBidAdapter.js * Update colossussspBidAdapter.js * Update colossussspBidAdapter_spec.js * use floor module * Revert "use floor module" This reverts commit f0c5c248627567e669d8eed4f2bb9a26a857e2ad. * use floor module * update to 5v * fix * add uid2 and bidFloor support * fix * add pbadslot support * fix conflicts * add onBidWon * refactor * add test for onBidWon() * fix * add group_id * Trigger circleci * fix * update user sync * fix window.location * fix test * updates * fix conflict * fix * updates * remove traffic param * add transactionId to request data for colossusssp adapter * Send tid in placements array * update user sync * updated tests * remove changes package-lock file * fix Co-authored-by: Vladislav Isaiko Co-authored-by: Aiholkin Co-authored-by: Mykhailo Yaremchuk --- modules/colossussspBidAdapter.js | 2 +- test/spec/modules/colossussspBidAdapter_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index c1560680a6e..75e73ffda89 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -214,7 +214,7 @@ export const spec = { }, getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncType = syncOptions.iframeEnabled ? 'html' : 'hms.gif'; + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; let syncUrl = G_URL_SYNC + `/${syncType}?pbjs=1`; if (gdprConsent && gdprConsent.consentString) { if (typeof gdprConsent.gdprApplies === 'boolean') { diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index 8b06868390a..71e94f0da32 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -299,8 +299,8 @@ describe('ColossussspAdapter', function () { expect(userSync).to.be.an('array').with.lengthOf(1); expect(userSync[0].type).to.exist; expect(userSync[0].url).to.exist; - expect(userSync[0].type).to.be.equal('hms.gif'); - expect(userSync[0].url).to.be.equal('https://sync.colossusssp.com/hms.gif?pbjs=1&gdpr=0&gdpr_consent=xxx&ccpa_consent=1YN-&coppa=0'); + expect(userSync[0].type).to.be.equal('image'); + expect(userSync[0].url).to.be.equal('https://sync.colossusssp.com/image?pbjs=1&gdpr=0&gdpr_consent=xxx&ccpa_consent=1YN-&coppa=0'); }); }); }); From 374aa26d592edc6906e8dc1f780d5fe1b2708148 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 13 Dec 2022 00:51:09 +0530 Subject: [PATCH 516/569] PubMatic Analytics Adapter:added parameters in logger call (#9328) * Logging floor related params in loger * Adding au and mt parameters in logger and tracker call * Added extra check Co-authored-by: pm-azhar-mulla Co-authored-by: pm-priyanka-deshmane --- modules/pubmaticAnalyticsAdapter.js | 43 ++++++++++++++- .../modules/pubmaticAnalyticsAdapter_spec.js | 52 ++++++++++++++++++- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index c8dc7cef15d..cae94f6fe7b 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -30,6 +30,11 @@ const DEFAULT_PUBLISHER_ID = 0; const DEFAULT_PROFILE_ID = 0; const DEFAULT_PROFILE_VERSION_ID = 0; const enc = window.encodeURIComponent; +const MEDIATYPE = { + BANNER: 0, + VIDEO: 1, + NATIVE: 2 +} /// /////////// VARIABLES ////////////// let publisherId = DEFAULT_PUBLISHER_ID; // int: mandatory @@ -89,6 +94,7 @@ function copyRequiredBidDetails(bid) { 'status', () => NO_BID, // default a bid to NO_BID until response is recieved or bid is timed out 'finalSource as source', 'params', + 'floorData', 'adUnit', () => pick(bid, [ 'adUnitCode', 'transactionId', @@ -153,6 +159,7 @@ function parseBidResponse(bid) { 'bidId', 'mediaType', 'params', + 'floorData', 'mi', 'regexPattern', () => bid.regexPattern || undefined, 'partnerImpId', // partner impression ID @@ -243,17 +250,28 @@ function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { 'af': bid.bidResponse ? (bid.bidResponse.mediaType || undefined) : undefined, 'ocpm': bid.bidResponse ? (bid.bidResponse.originalCpm || 0) : 0, 'ocry': bid.bidResponse ? (bid.bidResponse.originalCurrency || CURRENCY_USD) : CURRENCY_USD, - 'piid': bid.bidResponse ? (bid.bidResponse.partnerImpId || EMPTY_STRING) : EMPTY_STRING + 'piid': bid.bidResponse ? (bid.bidResponse.partnerImpId || EMPTY_STRING) : EMPTY_STRING, + 'frv': (s2sBidders.indexOf(bid.bidder) > -1) ? undefined : (bid.bidResponse ? (bid.bidResponse.floorData ? bid.bidResponse.floorData.floorRuleValue : undefined) : undefined) }); }); return partnerBids; }, []) } +function getAdUnitAdFormats(adUnit) { + var af = adUnit ? Object.keys(adUnit.mediaTypes || {}).map(format => MEDIATYPE[format.toUpperCase()]) : []; + return af; +} + +function getAdUnit(adUnits, adUnitId) { + return adUnits.filter(adUnit => (adUnit.divID && adUnit.divID == adUnitId) || (adUnit.code == adUnitId))[0]; +} + function executeBidsLoggerCall(e, highestCpmBids) { let auctionId = e.auctionId; let referrer = config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''; let auctionCache = cache.auctions[auctionId]; + let floorData = auctionCache.floorData; let outputObj = { s: [] }; let pixelURL = END_POINT_BID_LOGGER; @@ -283,12 +301,21 @@ function executeBidsLoggerCall(e, highestCpmBids) { return 0; })(); + if (floorData) { + outputObj['fmv'] = floorData.floorRequestData ? floorData.floorRequestData.modelVersion || undefined : undefined; + outputObj['ft'] = floorData.floorResponseData ? (floorData.floorResponseData.enforcements.enforceJS == false ? 0 : 1) : undefined; + } + outputObj.s = Object.keys(auctionCache.adUnitCodes).reduce(function(slotsArray, adUnitId) { let adUnit = auctionCache.adUnitCodes[adUnitId]; + let origAdUnit = getAdUnit(auctionCache.origAdUnits, adUnitId) || {}; let slotObject = { 'sn': adUnitId, + 'au': origAdUnit.adUnitId || adUnitId, + 'mt': getAdUnitAdFormats(origAdUnit), 'sz': adUnit.dimensions.map(e => e[0] + 'x' + e[1]), - 'ps': gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestCpmBids.filter(bid => bid.adUnitCode === adUnitId)) + 'ps': gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestCpmBids.filter(bid => bid.adUnitCode === adUnitId)), + 'fskp': floorData ? (floorData.floorRequestData ? (floorData.floorRequestData.skipped == false ? 0 : 1) : undefined) : undefined, }; slotsArray.push(slotObject); return slotsArray; @@ -318,6 +345,7 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { } const adapterName = getAdapterNameForAlias(winningBid.adapterCode || winningBid.bidder); + let origAdUnit = getAdUnit(cache.auctions[auctionId].origAdUnits, adUnitId) || {}; let pixelURL = END_POINT_WIN_BID_LOGGER; pixelURL += 'pubid=' + publisherId; pixelURL += '&purl=' + enc(config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''); @@ -327,6 +355,7 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { pixelURL += '&pid=' + enc(profileId); pixelURL += '&pdvid=' + enc(profileVersionId); pixelURL += '&slot=' + enc(adUnitId); + pixelURL += '&au=' + enc(origAdUnit.adUnitId || adUnitId); pixelURL += '&pn=' + enc(adapterName); pixelURL += '&bc=' + enc(winningBid.bidderCode || winningBid.bidder); pixelURL += '&en=' + enc(winningBid.bidResponse.bidPriceUSD); @@ -358,6 +387,8 @@ function auctionInitHandler(args) { 'bidderDonePendingCount', () => args.bidderRequests.length, ]); cacheEntry.adUnitCodes = {}; + cacheEntry.floorData = {}; + cacheEntry.origAdUnits = args.adUnits; cacheEntry.referer = args.bidderRequests[0].refererInfo.topmostLocation; cache.auctions[args.auctionId] = cacheEntry; } @@ -372,6 +403,9 @@ function bidRequestedHandler(args) { }; } cache.auctions[args.auctionId].adUnitCodes[bid.adUnitCode].bids[bid.bidId] = [copyRequiredBidDetails(bid)]; + if (bid.floorData) { + cache.auctions[args.auctionId].floorData['floorRequestData'] = bid.floorData; + } }) } @@ -386,6 +420,11 @@ function bidResponseHandler(args) { bid = copyRequiredBidDetails(args); cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId].push(bid); } + + if (args.floorData) { + cache.auctions[args.auctionId].floorData['floorResponseData'] = args.floorData; + } + bid.adId = args.adId; bid.source = formatSource(bid.source || args.source); setBidStatus(bid, args); diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index fa4519c84e1..4ad048fef9a 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -68,6 +68,14 @@ const BID = { 'hb_size': '640x480', 'hb_source': 'server' }, + 'floorData': { + 'cpmAfterAdjustments': 6.3, + 'enforcements': {'enforceJS': true, 'enforcePBS': false, 'floorDeals': false, 'bidAdjustment': true}, + 'floorCurrency': 'USD', + 'floorRule': 'banner', + 'floorRuleValue': 1.1, + 'floorValue': 1.1 + }, getStatusCode() { return 1; } @@ -200,7 +208,18 @@ const MOCK = { 'bidId': '3bd4ebb1c900e2', 'seatBidId': 'aaaa-bbbb-cccc-dddd', 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'floorData': { + 'fetchStatus': 'success', + 'floorMin': undefined, + 'floorProvider': 'pubmatic', + 'location': 'fetch', + 'modelTimestamp': undefined, + 'modelVersion': 'floorModelTest', + 'modelWeight': undefined, + 'skipRate': 0, + 'skipped': false + } } ], 'auctionStart': 1519149536560, @@ -343,10 +362,13 @@ describe('pubmatic analytics adapter', function () { expect(data.orig).to.equal('www.test.com'); expect(data.tst).to.equal(1519767016); expect(data.tgid).to.equal(15); + expect(data.fmv).to.equal('floorModelTest'); + expect(data.ft).to.equal(1); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].fskp).to.equal(0); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].ps.length).to.equal(1); @@ -370,8 +392,10 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].af).to.equal('video'); expect(data.s[0].ps[0].ocpm).to.equal(1.23); expect(data.s[0].ps[0].ocry).to.equal('USD'); + expect(data.s[0].ps[0].frv).to.equal(undefined); // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); + expect(data.s[1].fskp).to.equal(0); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -397,6 +421,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); + expect(data.s[1].ps[0].frv).to.equal(undefined); // tracker slot1 let firstTracker = requests[0].url; @@ -446,11 +471,14 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.pubid).to.equal('9999'); expect(data.pid).to.equal('1111'); + expect(data.fmv).to.equal('floorModelTest'); + expect(data.ft).to.equal(1); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); expect(data.tgid).to.equal(0); // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].fskp).to.equal(0); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].ps.length).to.equal(1); @@ -464,6 +492,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].af).to.equal('video'); expect(data.s[0].ps[0].ocpm).to.equal(1.23); expect(data.s[0].ps[0].ocry).to.equal('USD'); + expect(data.s[1].ps[0].frv).to.equal(undefined); // tracker slot1 let firstTracker = requests[0].url; expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); @@ -515,6 +544,8 @@ describe('pubmatic analytics adapter', function () { expect(data.pubid).to.equal('9999'); expect(data.pid).to.equal('1111'); expect(data.tgid).to.equal(0);// test group id should be between 0-15 else set to 0 + expect(data.fmv).to.equal('floorModelTest'); + expect(data.ft).to.equal(1); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); // slot 1 @@ -563,6 +594,7 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.tgid).to.equal(0);// test group id should be an INT between 0-15 else set to 0 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); + expect(data.s[1].fskp).to.equal(0); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -586,6 +618,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal(undefined); expect(data.s[1].ps[0].ocpm).to.equal(0); expect(data.s[1].ps[0].ocry).to.equal('USD'); + expect(data.s[1].ps[0].frv).to.equal(undefined); }); it('Logger: post-timeout check without bid response', function() { @@ -643,6 +676,7 @@ describe('pubmatic analytics adapter', function () { let request = requests[0]; let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); + expect(data.s[1].fskp).to.equal(0); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -667,6 +701,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); + expect(data.s[1].ps[0].frv).to.equal(undefined); }); it('Logger: currency conversion check', function() { @@ -748,6 +783,7 @@ describe('pubmatic analytics adapter', function () { expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); + expect(data.s[1].fskp).to.equal(0); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -772,6 +808,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); + expect(data.s[1].ps[0].frv).to.equal(undefined); expect(data.dvc).to.deep.equal({'plt': 2}); // respective tracker slot let firstTracker = requests[1].url; @@ -829,6 +866,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); expect(data.dvc).to.deep.equal({'plt': 1}); + expect(data.s[1].ps[0].frv).to.equal(undefined); // respective tracker slot let firstTracker = requests[1].url; expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); @@ -856,6 +894,7 @@ describe('pubmatic analytics adapter', function () { expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); + expect(data.s[1].fskp).to.equal(0); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -880,6 +919,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); + expect(data.s[1].ps[0].frv).to.equal(undefined); // respective tracker slot let firstTracker = requests[1].url; expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); @@ -979,11 +1019,14 @@ describe('pubmatic analytics adapter', function () { expect(data.orig).to.equal('www.test.com'); expect(data.tst).to.equal(1519767016); expect(data.tgid).to.equal(15); + expect(data.fmv).to.equal('floorModelTest'); + expect(data.ft).to.equal(1); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].fskp).to.equal(0); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].ps.length).to.equal(1); @@ -1007,9 +1050,11 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].af).to.equal('video'); expect(data.s[0].ps[0].ocpm).to.equal(1.23); expect(data.s[0].ps[0].ocry).to.equal('USD'); + expect(data.s[0].ps[0].frv).to.equal(1.1); // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); + expect(data.s[1].fskp).to.equal(0); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -1035,6 +1080,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); + expect(data.s[1].ps[0].frv).to.equal(undefined); // tracker slot1 let firstTracker = requests[0].url; @@ -1091,11 +1137,14 @@ describe('pubmatic analytics adapter', function () { expect(data.orig).to.equal('www.test.com'); expect(data.tst).to.equal(1519767016); expect(data.tgid).to.equal(15); + expect(data.fmv).to.equal('floorModelTest'); + expect(data.ft).to.equal(1); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].fskp).to.equal(0); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].ps.length).to.equal(1); @@ -1119,6 +1168,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].af).to.equal('video'); expect(data.s[0].ps[0].ocpm).to.equal(1.23); expect(data.s[0].ps[0].ocry).to.equal('USD'); + expect(data.s[0].ps[0].frv).to.equal(1.1); // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); From 05d32b281cd1d2d7c1fe49509b1503d59b9c8af2 Mon Sep 17 00:00:00 2001 From: Malkov Mikhail Date: Mon, 12 Dec 2022 22:59:03 +0300 Subject: [PATCH 517/569] Nextmillenium bid adapter: Collection of statistics data (#9265) * changed name company * changed name company in test * Added processing of a new group_id parameter * Added processing of a new group_id parameter * changed check parameters * fixed lint remarks * added test * fixed bug - lint * changed test * changed test - 2 * fixed bug - adapter * added logic for getting ad impressions * Collecting timeouts data * Collecting resaponses and no_bids data * changed a name function * added event bidRequested * added event bidRequested * added function initialization events * fixed bug * save * added tests * Added processing of the disabledSendingStatisticData parameter, which disables sending statistics data * changed the name of the variables --- modules/nextMillenniumBidAdapter.js | 125 +++++++++++++++- .../modules/nextMillenniumBidAdapter_spec.js | 133 ++++++++++++++++++ 2 files changed, 257 insertions(+), 1 deletion(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 802a8ac25b0..cf732d343e5 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -14,6 +14,7 @@ import { import CONSTANTS from '../src/constants.json'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; import * as events from '../src/events.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -22,6 +23,7 @@ import { getRefererInfo } from '../src/refererDetection.js'; const BIDDER_CODE = 'nextMillennium'; const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; const TEST_ENDPOINT = 'https://test.pbs.nextmillmedia.com/openrtb2/auction'; +const REPORT_ENDPOINT = 'https://report2.hb.brainlyads.com/statistics/metric'; const SYNC_ENDPOINT = 'https://cookies.nextmillmedia.com/sync?'; const TIME_TO_LIVE = 360; const VIDEO_PARAMS = [ @@ -29,11 +31,14 @@ const VIDEO_PARAMS = [ 'playbackmethod', 'protocols', 'startdelay' ]; +const sendingDataStatistic = initSendingDataStatistic(); +events.on(CONSTANTS.EVENTS.AUCTION_INIT, auctionInitHandler); + const EXPIRENCE_WURL = 20 * 60000; const wurlMap = {}; +cleanWurl(); events.on(CONSTANTS.EVENTS.BID_WON, bidWonHandler); -cleanWurl(); export const spec = { code: BIDDER_CODE, @@ -135,6 +140,7 @@ export const spec = { const urlParameters = parseUrl(getWindowTop().location.href).search; const isTest = urlParameters['pbs'] && urlParameters['pbs'] === 'test'; + const params = bid.params; requests.push({ method: 'POST', @@ -146,6 +152,7 @@ export const spec = { }, bidId, + params, auctionId, }); }); @@ -160,6 +167,7 @@ export const spec = { _each(response.seatbid, (resp) => { _each(resp.bid, (bid) => { const requestId = bidRequest.bidId; + const params = bidRequest.params; const auctionId = bidRequest.auctionId; const wurl = deepAccess(bid, 'ext.prebid.events.win'); addWurl({auctionId, requestId, wurl}); @@ -168,6 +176,7 @@ export const spec = { const bidResponse = { requestId, + params, cpm: bid.price, width: bid.w, height: bid.h, @@ -217,6 +226,41 @@ export const spec = { return pixels; }, + + getUrlPixelMetric(eventName, bid) { + const bidder = bid.bidder || bid.bidderCode; + if (bidder != BIDDER_CODE) return; + + let params; + if (bid.params) { + params = Array.isArray(bid.params) ? bid.params : [bid.params]; + } else { + if (Array.isArray(bid.bids)) params = bid.bids.map(bidI => bidI.params); + }; + + if (!params.length) return; + + const placementIdsArray = []; + const groupIdsArray = []; + params.forEach(paramsI => { + if (paramsI.group_id) { + groupIdsArray.push(paramsI.group_id); + } else { + if (paramsI.placement_id) placementIdsArray.push(paramsI.placement_id); + }; + }); + + const placementIds = (placementIdsArray.length && `&placements=${placementIdsArray.join(';')}`) || ''; + const groupIds = (groupIdsArray.length && `&groups=${groupIdsArray.join(';')}`) || ''; + + if (!(groupIds || placementIds)) { + return; + }; + + const url = `${REPORT_ENDPOINT}?event=${eventName}&bidder=${bidder}&source=pbjs${groupIds}${placementIds}`; + + return url; + }, }; function getAdEl(bid) { @@ -338,6 +382,10 @@ function bidWonHandler(bid) { }; } +function auctionInitHandler() { + sendingDataStatistic.initEvents(); +} + function cleanWurl() { const dateNow = Date.now(); Object.keys(wurlMap).forEach(key => { @@ -349,4 +397,79 @@ function cleanWurl() { setTimeout(cleanWurl, 60000); } +function initSendingDataStatistic() { + class SendingDataStatistic { + eventNames = [ + CONSTANTS.EVENTS.BID_TIMEOUT, + CONSTANTS.EVENTS.BID_RESPONSE, + CONSTANTS.EVENTS.BID_REQUESTED, + CONSTANTS.EVENTS.NO_BID, + ]; + + disabledSending = false; + enabledSending = false; + eventHendlers = {}; + + initEvents() { + this.disabledSending = !!config.getBidderConfig()?.nextMillennium?.disabledSendingStatisticData; + if (this.disabledSending) { + this.removeEvents(); + } else { + this.createEvents(); + }; + } + + createEvents() { + if (this.enabledSending) return; + + this.enabledSending = true; + for (let eventName of this.eventNames) { + if (!this.eventHendlers[eventName]) { + this.eventHendlers[eventName] = this.eventHandler(eventName); + }; + + events.on(eventName, this.eventHendlers[eventName]); + }; + } + + removeEvents() { + if (!this.enabledSending) return; + + this.enabledSending = false; + for (let eventName of this.eventNames) { + if (!this.eventHendlers[eventName]) continue; + + events.off(eventName, this.eventHendlers[eventName]); + }; + } + + eventHandler(eventName) { + const eventHandlerFunc = this.getEventHandler(eventName); + if (eventName == CONSTANTS.EVENTS.BID_TIMEOUT) { + return bids => { + if (this.disabledSending || !Array.isArray(bids)) return; + + for (let bid of bids) { + eventHandlerFunc(bid); + }; + } + }; + + return eventHandlerFunc; + } + + getEventHandler(eventName) { + return bid => { + if (this.disabledSending) return; + + const url = spec.getUrlPixelMetric(eventName, bid); + if (!url) return; + triggerPixel(url); + }; + } + }; + + return new SendingDataStatistic(); +} + registerBidder(spec); diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 2b5ae801489..3094c1349f7 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -276,4 +276,137 @@ describe('nextMillenniumBidAdapterTests', function() { expect(bid.height).to.equal(250); expect(bid.currency).to.equal('USD'); }); + + it('Check function of getting URL for sending statistics data', function() { + const dataForTests = [ + { + eventName: 'bidRequested', + bid: { + bidderCode: 'appnexus', + bids: [{bidder: 'appnexus', params: {}}], + }, + + expected: undefined, + }, + + { + eventName: 'bidRequested', + bid: { + bidderCode: 'appnexus', + bids: [{bidder: 'appnexus', params: {placement_id: '807'}}], + }, + + expected: undefined, + }, + + { + eventName: 'bidRequested', + bid: { + bidderCode: 'nextMillennium', + bids: [{bidder: 'nextMillennium', params: {placement_id: '807'}}], + }, + + expected: 'https://report2.hb.brainlyads.com/statistics/metric?event=bidRequested&bidder=nextMillennium&source=pbjs&placements=807', + }, + + { + eventName: 'bidRequested', + bid: { + bidderCode: 'nextMillennium', + bids: [ + {bidder: 'nextMillennium', params: {placement_id: '807'}}, + {bidder: 'nextMillennium', params: {placement_id: '111'}}, + ], + }, + + expected: 'https://report2.hb.brainlyads.com/statistics/metric?event=bidRequested&bidder=nextMillennium&source=pbjs&placements=807;111', + }, + + { + eventName: 'bidRequested', + bid: { + bidderCode: 'nextMillennium', + bids: [{bidder: 'nextMillennium', params: {placement_id: '807', group_id: '123'}}], + }, + + expected: 'https://report2.hb.brainlyads.com/statistics/metric?event=bidRequested&bidder=nextMillennium&source=pbjs&groups=123', + }, + + { + eventName: 'bidRequested', + bid: { + bidderCode: 'nextMillennium', + bids: [ + {bidder: 'nextMillennium', params: {placement_id: '807', group_id: '123'}}, + {bidder: 'nextMillennium', params: {group_id: '456'}}, + {bidder: 'nextMillennium', params: {placement_id: '222'}}, + ], + }, + + expected: 'https://report2.hb.brainlyads.com/statistics/metric?event=bidRequested&bidder=nextMillennium&source=pbjs&groups=123;456&placements=222', + }, + + { + eventName: 'bidResponse', + bid: { + bidderCode: 'appnexus', + }, + + expected: undefined, + }, + + { + eventName: 'bidResponse', + bid: { + bidderCode: 'nextMillennium', + params: {placement_id: '807'}, + }, + + expected: 'https://report2.hb.brainlyads.com/statistics/metric?event=bidResponse&bidder=nextMillennium&source=pbjs&placements=807', + }, + + { + eventName: 'noBid', + bid: { + bidder: 'appnexus', + }, + + expected: undefined, + }, + + { + eventName: 'noBid', + bid: { + bidder: 'nextMillennium', + params: {placement_id: '807'}, + }, + + expected: 'https://report2.hb.brainlyads.com/statistics/metric?event=noBid&bidder=nextMillennium&source=pbjs&placements=807', + }, + + { + eventName: 'bidTimeout', + bid: { + bidder: 'appnexus', + }, + + expected: undefined, + }, + + { + eventName: 'bidTimeout', + bid: { + bidder: 'nextMillennium', + params: {placement_id: '807'}, + }, + + expected: 'https://report2.hb.brainlyads.com/statistics/metric?event=bidTimeout&bidder=nextMillennium&source=pbjs&placements=807', + }, + ]; + + for (let {eventName, bid, expected} of dataForTests) { + const url = spec.getUrlPixelMetric(eventName, bid); + expect(url).to.equal(expected); + }; + }) }); From c1a193a116086583054fff46af67d0dbb386277f Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Mon, 12 Dec 2022 15:18:21 -0500 Subject: [PATCH 518/569] Video Module: Ad Queueing (#9226) * adds queue coordinator * tests module * test setAdTag * tests integration * updates examples * documents the event * loads get queued always * updates tests * decouples register from init * updates tests * Revert "updates tests" This reverts commit 1616dfad2bba34a10b3f20fb5680baea7ce11fa1. * updates tests --- .../videoModule/jwplayer/bidMarkedAsUsed.html | 6 +- .../videoModule/jwplayer/eventListeners.html | 7 +- .../jwplayer/gamAdServerMediation.html | 6 +- .../videoModule/jwplayer/mediaMetadata.html | 3 +- .../videoModule/jwplayer/playlist.html | 3 +- .../videoModule/videojs/bidMarkedAsUsed.html | 5 +- .../videoModule/videojs/eventListeners.html | 4 + .../videojs/gamAdServerMediation.html | 3 +- .../videoModule/videojs/playlist.html | 4 +- .../constants/{enums.js => constants.js} | 2 + libraries/video/constants/events.js | 1 + libraries/video/shared/helpers.js | 5 + libraries/video/shared/parentModule.js | 1 - modules/jwplayerVideoProvider.js | 2 +- modules/videoModule/adQueue.js | 63 +++++++ modules/videoModule/addingSubmodule.md | 7 + modules/videoModule/coreVideo.js | 6 + modules/videoModule/index.js | 35 ++-- modules/videojsVideoProvider.js | 2 +- test/spec/modules/videoModule/adQueue_spec.js | 172 ++++++++++++++++++ test/spec/modules/videoModule/pbVideo_spec.js | 34 ++-- .../videoModule/shared/parentModule_spec.js | 1 - .../submodules/jwplayerVideoProvider_spec.js | 2 +- 23 files changed, 325 insertions(+), 49 deletions(-) rename libraries/video/constants/{enums.js => constants.js} (65%) create mode 100644 libraries/video/shared/helpers.js create mode 100644 modules/videoModule/adQueue.js create mode 100644 test/spec/modules/videoModule/adQueue_spec.js diff --git a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html index 542cab4202e..044b4295e67 100644 --- a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html @@ -76,10 +76,6 @@ console.log('player setup failed: ', e); }); - pbjs.onEvent('videoPlaybackRequest', (e) => { - pbjs.requestBids(adUnits); - }); - pbjs.onEvent('videoBidError', e => { console.log('An Ad Error came from a Bid: ', e); }); @@ -87,6 +83,8 @@ pbjs.onEvent('videoBidImpression', e => { console.log('An Ad Impression came from a Bid: ', e); }); + + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/eventListeners.html b/integrationExamples/videoModule/jwplayer/eventListeners.html index b4f5aaa5443..0b43ff44d6c 100644 --- a/integrationExamples/videoModule/jwplayer/eventListeners.html +++ b/integrationExamples/videoModule/jwplayer/eventListeners.html @@ -87,7 +87,6 @@ pbjs.onEvent('videoPlaybackRequest', (e) => { console.log('videos pb playbackRequest: ', e); - pbjs.requestBids(adUnits); }); pbjs.onEvent('videoAutostartBlocked', (e) => { @@ -222,6 +221,10 @@ console.log('videos pb auction ad load attempt: ', e); }); + pbjs.onEvent('videoAuctionAdLoadQueued', (e) => { + console.log('videos pb auction ad load queued: ', e); + }); + pbjs.onEvent('videoAuctionAdLoadAttempt', event => { console.log('The Video Module is attempting to load an ad into the player! \n', event); }); @@ -233,6 +236,8 @@ pbjs.onEvent('videoBidError', event => { console.log('The ad error resulted from a bid! \n', event); }); + + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html index 4612d03f336..296a3265bd1 100644 --- a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html +++ b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html @@ -94,10 +94,6 @@ console.log('player setup failed: ', e); }); - pbjs.onEvent('videoPlaybackRequest', (e) => { - pbjs.requestBids(adUnits); - }); - pbjs.onEvent('videoAdRequest', (e) => { console.log('videos pb ad request: ', e); }); @@ -109,6 +105,8 @@ pbjs.onEvent('videoBidImpression', e => { console.log('An Ad Impression came from a Bid: ', e); }); + + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/mediaMetadata.html b/integrationExamples/videoModule/jwplayer/mediaMetadata.html index 97e35c4de0d..815f5c4d6d7 100644 --- a/integrationExamples/videoModule/jwplayer/mediaMetadata.html +++ b/integrationExamples/videoModule/jwplayer/mediaMetadata.html @@ -56,7 +56,6 @@ pbjs.onEvent('videoSetupComplete', e => { console.log('player setup complete: ', e); - pbjs.requestBids(adUnits); }); pbjs.onEvent('videoSetupFailed', e => { @@ -66,6 +65,8 @@ pbjs.onEvent('videoContentLoaded', (e) => { console.log('videos pb contentLoaded: ', e); }); + + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/playlist.html b/integrationExamples/videoModule/jwplayer/playlist.html index eb9a9b12700..b77a1ec05fc 100644 --- a/integrationExamples/videoModule/jwplayer/playlist.html +++ b/integrationExamples/videoModule/jwplayer/playlist.html @@ -97,7 +97,6 @@ // request a bid when media is loaded pbjs.onEvent('videoContentLoaded', (e) => { console.log('videos pb contentLoaded: ', e); - pbjs.requestBids(adUnits); }); pbjs.onEvent('videoComplete', (e) => { @@ -107,6 +106,8 @@ pbjs.onEvent('videoPlaylistComplete', (e) => { console.log('videos pb playlistComplete: ', e); }); + + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html b/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html index 3c21130e8f5..b645a58a4fc 100644 --- a/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html @@ -101,10 +101,6 @@ console.log('player setup failed: ', e); }); - pbjs.onEvent('videoContentLoaded', (e) => { - pbjs.requestBids(adUnits); - }); - pbjs.onEvent('videoBidError', e => { console.log('An Ad Error came from a Bid: ', e); }); @@ -113,6 +109,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/eventListeners.html b/integrationExamples/videoModule/videojs/eventListeners.html index 63de878715f..16edaf4da41 100644 --- a/integrationExamples/videoModule/videojs/eventListeners.html +++ b/integrationExamples/videoModule/videojs/eventListeners.html @@ -209,6 +209,10 @@ console.log('videos pb auction ad load attempt: ', e); }); + pbjs.onEvent('videoAuctionAdLoadQueued', (e) => { + console.log('videos pb auction ad load queued: ', e); + }); + pbjs.onEvent('videoAuctionAdLoadAbort', (e) => { console.log('videos pb auction ad load attempt: ', e); }); diff --git a/integrationExamples/videoModule/videojs/gamAdServerMediation.html b/integrationExamples/videoModule/videojs/gamAdServerMediation.html index 72deba861bf..9efd77e9681 100644 --- a/integrationExamples/videoModule/videojs/gamAdServerMediation.html +++ b/integrationExamples/videoModule/videojs/gamAdServerMediation.html @@ -96,8 +96,6 @@ pbjs.addAdUnits(adUnits); - pbjs.requestBids(adUnits); - pbjs.onEvent('videoSetupComplete', e => { // Load media with its Metadata when the video player is done instantiating. videojs('player').loadMedia({ @@ -125,6 +123,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/playlist.html b/integrationExamples/videoModule/videojs/playlist.html index a9354e06c83..a9e3d1bc99b 100644 --- a/integrationExamples/videoModule/videojs/playlist.html +++ b/integrationExamples/videoModule/videojs/playlist.html @@ -126,15 +126,15 @@ console.log('videos pb playlist: ', e); }); - // request a bid when media is loaded pbjs.onEvent('videoContentLoaded', (e) => { console.log('videos pb contentLoaded: ', e); - pbjs.requestBids(adUnits); }); pbjs.onEvent('videoComplete', (e) => { console.log('videos pb complete: ', e); }); + + pbjs.requestBids(adUnits); }); diff --git a/libraries/video/constants/enums.js b/libraries/video/constants/constants.js similarity index 65% rename from libraries/video/constants/enums.js rename to libraries/video/constants/constants.js index b0755020580..55e3785ccc2 100644 --- a/libraries/video/constants/enums.js +++ b/libraries/video/constants/constants.js @@ -1,3 +1,5 @@ +export const videoKey = 'video'; + export const PLAYBACK_MODE = { VOD: 0, LIVE: 1, diff --git a/libraries/video/constants/events.js b/libraries/video/constants/events.js index 5be594b1b48..b7932adf621 100644 --- a/libraries/video/constants/events.js +++ b/libraries/video/constants/events.js @@ -52,6 +52,7 @@ export const allVideoEvents = [ ]; export const AUCTION_AD_LOAD_ATTEMPT = 'auctionAdLoadAttempt'; +export const AUCTION_AD_LOAD_QUEUED = 'auctionAdLoadQueued'; export const AUCTION_AD_LOAD_ABORT = 'auctionAdLoadAbort'; export const BID_IMPRESSION = 'bidImpression'; export const BID_ERROR = 'bidError'; diff --git a/libraries/video/shared/helpers.js b/libraries/video/shared/helpers.js new file mode 100644 index 00000000000..1b87f3bf1f3 --- /dev/null +++ b/libraries/video/shared/helpers.js @@ -0,0 +1,5 @@ +import { videoKey } from '../constants/constants.js' + +export function getExternalVideoEventName(eventName) { + return videoKey + eventName.replace(/^./, eventName[0].toUpperCase()); +} diff --git a/libraries/video/shared/parentModule.js b/libraries/video/shared/parentModule.js index e96113fd894..06c71ebd75b 100644 --- a/libraries/video/shared/parentModule.js +++ b/libraries/video/shared/parentModule.js @@ -72,7 +72,6 @@ export function SubmoduleBuilder(submoduleDirectory_, sharedUtils_) { } const submodule = submoduleFactory(config, sharedUtils); - submodule && submodule.init && submodule.init(); return submodule; } diff --git a/modules/jwplayerVideoProvider.js b/modules/jwplayerVideoProvider.js index d606e8121a7..379d8063c42 100644 --- a/modules/jwplayerVideoProvider.js +++ b/modules/jwplayerVideoProvider.js @@ -7,7 +7,7 @@ import { AUTOSTART_BLOCKED, PLAY_ATTEMPT_FAILED, CONTENT_LOADED, PLAY, PAUSE, BUFFER, TIME, SEEK_START, SEEK_END, MUTE, VOLUME, RENDITION_UPDATE, ERROR, COMPLETE, PLAYLIST_COMPLETE, FULLSCREEN, PLAYER_RESIZE, VIEWABLE, CAST } from '../libraries/video/constants/events.js'; -import { PLAYBACK_MODE } from '../libraries/video/constants/enums.js'; +import { PLAYBACK_MODE } from '../libraries/video/constants/constants.js'; import stateFactory from '../libraries/video/shared/state.js'; import { JWPLAYER_VENDOR } from '../libraries/video/constants/vendorCodes.js'; import { getEventHandler } from '../libraries/video/shared/eventHandler.js'; diff --git a/modules/videoModule/adQueue.js b/modules/videoModule/adQueue.js new file mode 100644 index 00000000000..a6da3752f6e --- /dev/null +++ b/modules/videoModule/adQueue.js @@ -0,0 +1,63 @@ +import { AD_BREAK_END, AUCTION_AD_LOAD_ATTEMPT, AUCTION_AD_LOAD_QUEUED, SETUP_COMPLETE } from '../../libraries/video/constants/events.js' +import { getExternalVideoEventName } from '../../libraries/video/shared/helpers.js' + +export function AdQueueCoordinator(videoCore, pbEvents) { + const storage = {}; + + function registerProvider(divId) { + storage[divId] = []; + videoCore.onEvents([SETUP_COMPLETE], onSetupComplete, divId); + } + + function queueAd(adTagUrl, divId, options) { + const queue = storage[divId]; + if (queue) { + queue.push({adTagUrl, options}); + triggerEvent(AUCTION_AD_LOAD_QUEUED, adTagUrl, options); + } else { + loadAd(divId, adTagUrl, options); + } + } + + return { + registerProvider, + queueAd + }; + + function onSetupComplete(eventName, eventPayload) { + const divId = eventPayload.divId; + videoCore.offEvents([SETUP_COMPLETE], onSetupComplete, divId); + loadQueuedAd(divId); + } + + function onAdBreakEnd(eventName, eventPayload) { + loadQueuedAd(eventPayload.divId); + } + + function loadQueuedAd(divId) { + videoCore.offEvents([AD_BREAK_END], onAdBreakEnd, divId); + const adQueue = storage[divId]; + if (!adQueue) { + return; + } + + if (!adQueue.length) { + delete storage[divId]; + return; + } + + const queuedAd = adQueue.shift(); + videoCore.onEvents([AD_BREAK_END], onAdBreakEnd, divId); + loadAd(divId, queuedAd.adTagUrl, queuedAd.options); + } + + function loadAd(divId, adTagUrl, options) { + triggerEvent(AUCTION_AD_LOAD_ATTEMPT, adTagUrl, options); + videoCore.setAdTagUrl(adTagUrl, divId, options); + } + + function triggerEvent(eventName, adTagUrl, options) { + const payload = Object.assign({ adTagUrl }, options); + pbEvents.emit(getExternalVideoEventName(eventName), payload); + } +} diff --git a/modules/videoModule/addingSubmodule.md b/modules/videoModule/addingSubmodule.md index 874718be1a7..2f880fdef3a 100644 --- a/modules/videoModule/addingSubmodule.md +++ b/modules/videoModule/addingSubmodule.md @@ -481,6 +481,13 @@ No additional params. | adTagUrl | string | The URL for the ad tag associated with the given ad event | | adUnitCode | string | Unique identifier that was used when creating the ad unit. | +###### AUCTION_AD_LOAD_QUEUED + +| argument name | type | description | +| ------------- | ---- | ----------- | +| adTagUrl | string | The URL for the ad tag associated with the given ad event | +| adUnitCode | string | Unique identifier that was used when creating the ad unit. | + ###### AUCTION_AD_LOAD_ABORT | argument name | type | description | diff --git a/modules/videoModule/coreVideo.js b/modules/videoModule/coreVideo.js index c06482b0b36..ce66acc2b02 100644 --- a/modules/videoModule/coreVideo.js +++ b/modules/videoModule/coreVideo.js @@ -127,6 +127,11 @@ export function VideoCore(parentModule_) { } catch (e) {} } + function initProvider(divId) { + const submodule = parentModule.getSubmodule(divId); + submodule && submodule.init && submodule.init(); + } + /** * @name VideoCore#getOrtbVideo * @summary Obtains the oRTB Video params for a player's current video session. @@ -208,6 +213,7 @@ export function VideoCore(parentModule_) { return { registerProvider, + initProvider, getOrtbVideo, getOrtbContent, setAdTagUrl, diff --git a/modules/videoModule/index.js b/modules/videoModule/index.js index 33ce3248d91..fb3c621918d 100644 --- a/modules/videoModule/index.js +++ b/modules/videoModule/index.js @@ -5,23 +5,31 @@ import { mergeDeep } from '../../src/utils.js'; import { getGlobal } from '../../src/prebidGlobal.js'; import CONSTANTS from '../../src/constants.json'; import { - videoEvents, AUCTION_AD_LOAD_ATTEMPT, AD_IMPRESSION, AD_ERROR, BID_IMPRESSION, BID_ERROR, AUCTION_AD_LOAD_ABORT + videoEvents, + AUCTION_AD_LOAD_ATTEMPT, + AD_IMPRESSION, + AD_ERROR, + BID_IMPRESSION, + BID_ERROR, + AUCTION_AD_LOAD_ABORT, + AUCTION_AD_LOAD_QUEUED } from '../../libraries/video/constants/events.js' import { PLACEMENT } from '../../libraries/video/constants/ortb.js'; +import { videoKey } from '../../libraries/video/constants/constants.js' import { videoCoreFactory } from './coreVideo.js'; import { gamSubmoduleFactory } from './gamAdServerSubmodule.js'; import { videoImpressionVerifierFactory } from './videoImpressionVerifier.js'; - -const videoKey = 'video'; +import { AdQueueCoordinator } from './adQueue.js'; +import { getExternalVideoEventName } from '../../libraries/video/shared/helpers.js' const allVideoEvents = Object.keys(videoEvents).map(eventKey => videoEvents[eventKey]); -events.addEvents(allVideoEvents.concat([AUCTION_AD_LOAD_ATTEMPT, AUCTION_AD_LOAD_ABORT, BID_IMPRESSION, BID_ERROR]).map(getExternalVideoEventName)); +events.addEvents(allVideoEvents.concat([AUCTION_AD_LOAD_ATTEMPT, AUCTION_AD_LOAD_QUEUED, AUCTION_AD_LOAD_ABORT, BID_IMPRESSION, BID_ERROR]).map(getExternalVideoEventName)); /** * This module adds User Video support to prebid.js * @module modules/videoModule */ -export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvents_, gamAdServerFactory_, videoImpressionVerifierFactory_) { +export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvents_, gamAdServerFactory_, videoImpressionVerifierFactory_, adQueueCoordinator_) { const videoCore = videoCore_; const getConfig = getConfig_; const pbGlobal = pbGlobal_; @@ -29,6 +37,7 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent const pbEvents = pbEvents_; const videoEvents = videoEvents_; const gamAdServerFactory = gamAdServerFactory_; + const adQueueCoordinator = adQueueCoordinator_; let gamSubmodule; let mainContentDivId; let contentEnrichmentEnabled = true; @@ -40,10 +49,13 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent videoImpressionVerifier = videoImpressionVerifierFactory(!!cache); getConfig(videoKey, ({ video }) => { video.providers.forEach(provider => { + const divId = provider.divId; videoCore.registerProvider(provider); + adQueueCoordinator.registerProvider(divId); + videoCore.initProvider(divId); videoCore.onEvents(videoEvents, (type, payload) => { pbEvents.emit(getExternalVideoEventName(type), payload); - }, provider.divId); + }, divId); const adServerConfig = provider.adServer; if (!gamSubmodule && adServerConfig) { @@ -199,9 +211,7 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent // options: adXml, winner, adUnitCode, function loadAdTag(adTagUrl, divId, options) { - const payload = Object.assign({ adTagUrl }, options); - pbEvents.emit(getExternalVideoEventName(AUCTION_AD_LOAD_ATTEMPT), payload); - videoCore.setAdTagUrl(adTagUrl, divId, options); + adQueueCoordinator.queueAd(adTagUrl, divId, options); } function triggerVideoBidEvent(eventName, adEventPayload) { @@ -230,15 +240,12 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent export function pbVideoFactory() { const videoCore = videoCoreFactory(); + const adQueueCoordinator = AdQueueCoordinator(videoCore, events); const pbGlobal = getGlobal(); - const pbVideo = PbVideo(videoCore, config.getConfig, pbGlobal, events, allVideoEvents, gamSubmoduleFactory, videoImpressionVerifierFactory); + const pbVideo = PbVideo(videoCore, config.getConfig, pbGlobal, events, allVideoEvents, gamSubmoduleFactory, videoImpressionVerifierFactory, adQueueCoordinator); pbVideo.init(); pbGlobal.videoModule = pbVideo; return pbVideo; } -function getExternalVideoEventName(eventName) { - return videoKey + eventName.replace(/^./, eventName[0].toUpperCase()); -} - pbVideoFactory(); diff --git a/modules/videojsVideoProvider.js b/modules/videojsVideoProvider.js index a142e0fbb84..cc17758bd72 100644 --- a/modules/videojsVideoProvider.js +++ b/modules/videojsVideoProvider.js @@ -11,7 +11,7 @@ import { import { VIDEO_JS_VENDOR } from '../libraries/video/constants/vendorCodes.js'; import { submodule } from '../src/hook.js'; import stateFactory from '../libraries/video/shared/state.js'; -import { PLAYBACK_MODE } from '../libraries/video/constants/enums.js'; +import { PLAYBACK_MODE } from '../libraries/video/constants/constants.js'; import { getEventHandler } from '../libraries/video/shared/eventHandler.js'; /* diff --git a/test/spec/modules/videoModule/adQueue_spec.js b/test/spec/modules/videoModule/adQueue_spec.js new file mode 100644 index 00000000000..4002e0b6dcc --- /dev/null +++ b/test/spec/modules/videoModule/adQueue_spec.js @@ -0,0 +1,172 @@ +import { expect } from 'chai'; +import { AdQueueCoordinator } from '../../../../modules/videoModule/adQueue.js'; +import { AD_BREAK_END, SETUP_COMPLETE } from '../../../../libraries/video/constants/events.js' + +const testId = 'testId'; +describe('Ad Queue Coordinator', function () { + const mockVideoCoreFactory = function () { + return { + onEvents: sinon.spy(), + offEvents: sinon.spy(), + setAdTagUrl: sinon.spy(), + } + }; + + const mockEventsFactory = function () { + return { + emit: sinon.spy() + }; + }; + + describe('Before Provider Setup Complete', function () { + it('should push ad to queue', function () { + const mockVideoCore = mockVideoCoreFactory(); + const mockEvents = mockEventsFactory(); + const coordinator = AdQueueCoordinator(mockVideoCore, mockEvents); + coordinator.registerProvider(testId); + coordinator.queueAd('testAdTag', testId, { param: {} }); + + expect(mockEvents.emit.calledOnce).to.be.true; + let emitArgs = mockEvents.emit.firstCall.args; + expect(emitArgs[0]).to.be.equal('videoAuctionAdLoadQueued'); + expect(mockVideoCore.setAdTagUrl.called).to.be.false; + }); + }); + + describe('After Provider Setup Complete', function () { + it('should load from ad queue', function () { + const mockVideoCore = mockVideoCoreFactory(); + const mockEvents = mockEventsFactory(); + let setupComplete; + mockVideoCore.onEvents = function(events, callback, id) { + if (events[0] === SETUP_COMPLETE && id === testId) { + setupComplete = callback; + } + }; + const coordinator = AdQueueCoordinator(mockVideoCore, mockEvents); + coordinator.registerProvider(testId); + coordinator.queueAd('testAdTag', testId, { param: {} }); + + expect(mockEvents.emit.calledOnce).to.be.true; + let emitArgs = mockEvents.emit.firstCall.args; + expect(emitArgs[0]).to.be.equal('videoAuctionAdLoadQueued'); + + setupComplete('', { divId: testId }); + expect(mockEvents.emit.calledTwice).to.be.true; + emitArgs = mockEvents.emit.secondCall.args; + expect(emitArgs[0]).to.be.equal('videoAuctionAdLoadAttempt'); + expect(mockVideoCore.setAdTagUrl.calledOnce).to.be.true; + }); + + it('should load ads without queueing', function () { + const mockVideoCore = mockVideoCoreFactory(); + const mockEvents = mockEventsFactory(); + let setupComplete; + mockVideoCore.onEvents = function(events, callback, id) { + if (events[0] === SETUP_COMPLETE && id === testId) { + setupComplete = callback; + } + }; + const coordinator = AdQueueCoordinator(mockVideoCore, mockEvents); + coordinator.registerProvider(testId); + + setupComplete('', { divId: testId }); + + coordinator.queueAd('testAdTag', testId, { param: {} }); + expect(mockEvents.emit.calledOnce).to.be.true; + let emitArgs = mockEvents.emit.firstCall.args; + expect(emitArgs[0]).to.be.equal('videoAuctionAdLoadAttempt'); + expect(mockVideoCore.setAdTagUrl.calledOnce).to.be.true; + }); + }); + + describe('On Ad Break End', function () { + it('should load from queue', function () { + const mockVideoCore = mockVideoCoreFactory(); + const mockEvents = mockEventsFactory(); + let setupComplete; + let adBreakEnd; + + mockVideoCore.onEvents = function(events, callback, id) { + if (events[0] === SETUP_COMPLETE && id === testId) { + setupComplete = callback; + } + + if (events[0] === AD_BREAK_END && id === testId) { + adBreakEnd = callback; + } + }; + + const coordinator = AdQueueCoordinator(mockVideoCore, mockEvents); + coordinator.registerProvider(testId); + coordinator.queueAd('testAdTag', testId); + coordinator.queueAd('testAdTag2', testId); + coordinator.queueAd('testAdTag3', testId); + + mockEvents.emit.resetHistory(); + + setupComplete('', { divId: testId }); + + expect(mockEvents.emit.calledOnce).to.be.true; + let emitArgs = mockEvents.emit.firstCall.args; + expect(emitArgs[0]).to.be.equal('videoAuctionAdLoadAttempt'); + expect(mockVideoCore.setAdTagUrl.calledOnce).to.be.true; + let setAdTagArgs = mockVideoCore.setAdTagUrl.firstCall.args; + expect(setAdTagArgs[0]).to.be.equal('testAdTag'); + + adBreakEnd('', { divId: testId }); + + expect(mockEvents.emit.calledTwice).to.be.true; + emitArgs = mockEvents.emit.secondCall.args; + expect(emitArgs[0]).to.be.equal('videoAuctionAdLoadAttempt'); + expect(mockVideoCore.setAdTagUrl.calledTwice).to.be.true; + setAdTagArgs = mockVideoCore.setAdTagUrl.secondCall.args; + expect(setAdTagArgs[0]).to.be.equal('testAdTag2'); + + adBreakEnd('', { divId: testId }); + + expect(mockEvents.emit.calledThrice).to.be.true; + emitArgs = mockEvents.emit.thirdCall.args; + expect(emitArgs[0]).to.be.equal('videoAuctionAdLoadAttempt'); + expect(mockVideoCore.setAdTagUrl.calledThrice).to.be.true; + setAdTagArgs = mockVideoCore.setAdTagUrl.thirdCall.args; + expect(setAdTagArgs[0]).to.be.equal('testAdTag3'); + + adBreakEnd('', { divId: testId }); + + expect(mockEvents.emit.calledThrice).to.be.true; + expect(mockVideoCore.setAdTagUrl.calledThrice).to.be.true; + }); + + it('should stop responding to AdBreakEnd when queue is empty', function () { + const mockVideoCore = mockVideoCoreFactory(); + let setupComplete; + let adBreakEnd; + + mockVideoCore.onEvents = function(events, callback, id) { + if (events[0] === SETUP_COMPLETE && id === testId) { + setupComplete = callback; + } + + if (events[0] === AD_BREAK_END && id === testId) { + adBreakEnd = callback; + } + }; + + const coordinator = AdQueueCoordinator(mockVideoCore, mockEventsFactory()); + coordinator.registerProvider(testId); + coordinator.queueAd('testAdTag', testId); + coordinator.queueAd('testAdTag2', testId); + coordinator.queueAd('testAdTag3', testId); + + setupComplete('', { divId: testId }); + adBreakEnd('', { divId: testId }); + adBreakEnd('', { divId: testId }); + expect(mockVideoCore.setAdTagUrl.calledThrice).to.be.true; + adBreakEnd('', { divId: testId }); + expect(mockVideoCore.setAdTagUrl.calledThrice).to.be.true; + adBreakEnd('', { divId: testId }); + expect(mockVideoCore.setAdTagUrl.calledThrice).to.be.true; + }); + }); +}); diff --git a/test/spec/modules/videoModule/pbVideo_spec.js b/test/spec/modules/videoModule/pbVideo_spec.js index cfd34ebd706..41780a007dd 100644 --- a/test/spec/modules/videoModule/pbVideo_spec.js +++ b/test/spec/modules/videoModule/pbVideo_spec.js @@ -14,12 +14,15 @@ let gamSubmoduleMock; let gamSubmoduleFactoryMock; let videoImpressionVerifierFactoryMock; let videoImpressionVerifierMock; +let adQueueCoordinatorMock; +let adQueueCoordinatorFactoryMock; function resetTestVars() { ortbVideoMock = {}; ortbContentMock = {}; videoCoreMock = { registerProvider: sinon.spy(), + initProvider: sinon.spy(), onEvents: sinon.spy(), getOrtbVideo: () => ortbVideoMock, getOrtbContent: () => ortbContentMock, @@ -54,9 +57,16 @@ function resetTestVars() { }; videoImpressionVerifierFactoryMock = () => videoImpressionVerifierMock; + + adQueueCoordinatorMock = { + registerProvider: sinon.spy(), + queueAd: sinon.spy() + }; + + adQueueCoordinatorFactoryMock = () => adQueueCoordinatorMock; } -let pbVideoFactory = (videoCore, getConfig, pbGlobal, pbEvents, videoEvents, gamSubmoduleFactory, videoImpressionVerifierFactory) => { +let pbVideoFactory = (videoCore, getConfig, pbGlobal, pbEvents, videoEvents, gamSubmoduleFactory, videoImpressionVerifierFactory, adQueueCoordinator) => { const pbVideo = PbVideo( videoCore || videoCoreMock, getConfig || getConfigMock, @@ -64,7 +74,8 @@ let pbVideoFactory = (videoCore, getConfig, pbGlobal, pbEvents, videoEvents, gam pbEvents || pbEventsMock, videoEvents || videoEventsMock, gamSubmoduleFactory || gamSubmoduleFactoryMock, - videoImpressionVerifierFactory || videoImpressionVerifierFactoryMock + videoImpressionVerifierFactory || videoImpressionVerifierFactoryMock, + adQueueCoordinator || adQueueCoordinatorMock ); pbVideo.init(); return pbVideo; @@ -207,6 +218,7 @@ describe('Prebid Video', function () { beforeEach(() => { gamSubmoduleMock.getAdTagUrl.resetHistory(); videoCoreMock.setAdTagUrl.resetHistory(); + adQueueCoordinatorMock.queueAd.resetHistory(); }); let beforeBidRequestCallback; @@ -250,10 +262,10 @@ describe('Prebid Video', function () { pbVideoFactory(null, getConfig, pbGlobal, pbEvents, null, gamSubmoduleFactory); beforeBidRequestCallback(() => {}, {}); auctionEndCallback(auctionResults); - expect(videoCoreMock.setAdTagUrl.calledOnce).to.be.true; - expect(videoCoreMock.setAdTagUrl.args[0][0]).to.be.equal(expectedAdTag); - expect(videoCoreMock.setAdTagUrl.args[0][1]).to.be.equal(expectedDivId); - expect(videoCoreMock.setAdTagUrl.args[0][2]).to.have.property('adUnitCode', expectedAdUnitCode); + expect(adQueueCoordinatorMock.queueAd.calledOnce).to.be.true; + expect(adQueueCoordinatorMock.queueAd.args[0][0]).to.be.equal(expectedAdTag); + expect(adQueueCoordinatorMock.queueAd.args[0][1]).to.be.equal(expectedDivId); + expect(adQueueCoordinatorMock.queueAd.args[0][2]).to.have.property('adUnitCode', expectedAdUnitCode); }); it('should load ad tag from highest bid when ad server is not configured', function () { @@ -275,11 +287,11 @@ describe('Prebid Video', function () { pbVideoFactory(null, () => ({ providers: [] }), pbGlobal, pbEvents); beforeBidRequestCallback(() => {}, {}); auctionEndCallback(auctionResults); - expect(videoCoreMock.setAdTagUrl.calledOnce).to.be.true; - expect(videoCoreMock.setAdTagUrl.args[0][0]).to.be.equal(expectedVastUrl); - expect(videoCoreMock.setAdTagUrl.args[0][1]).to.be.equal(expectedDivId); - expect(videoCoreMock.setAdTagUrl.args[0][2]).to.have.property('adUnitCode', expectedAdUnitCode); - expect(videoCoreMock.setAdTagUrl.args[0][2]).to.have.property('adXml', expectedVastXml); + expect(adQueueCoordinatorMock.queueAd.calledOnce).to.be.true; + expect(adQueueCoordinatorMock.queueAd.args[0][0]).to.be.equal(expectedVastUrl); + expect(adQueueCoordinatorMock.queueAd.args[0][1]).to.be.equal(expectedDivId); + expect(adQueueCoordinatorMock.queueAd.args[0][2]).to.have.property('adUnitCode', expectedAdUnitCode); + expect(adQueueCoordinatorMock.queueAd.args[0][2]).to.have.property('adXml', expectedVastXml); }); }); diff --git a/test/spec/modules/videoModule/shared/parentModule_spec.js b/test/spec/modules/videoModule/shared/parentModule_spec.js index 1e8e7fda380..e3e4cfb7f3f 100644 --- a/test/spec/modules/videoModule/shared/parentModule_spec.js +++ b/test/spec/modules/videoModule/shared/parentModule_spec.js @@ -62,7 +62,6 @@ describe('Submodule Builder', function () { it('should instantiate the submodule, when supported', function () { const submodule = submoduleBuilder.build(vendorCode2); - expect(initSpy.calledOnce).to.be.true; expect(submodule).to.be.equal(submodule2); }); diff --git a/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js b/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js index 399c115b820..4377e989851 100644 --- a/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js +++ b/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js @@ -14,7 +14,7 @@ import { SETUP_COMPLETE, SETUP_FAILED, PLAY, AD_IMPRESSION, videoEvents } from 'libraries/video/constants/events.js'; -import { PLAYBACK_MODE } from 'libraries/video/constants/enums.js'; +import { PLAYBACK_MODE } from 'libraries/video/constants/constants.js'; function getPlayerMock() { return makePlayerFactoryMock({ From 182f08e314cb1d7ab9a5fc6cf86ed0179684474b Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Tue, 13 Dec 2022 16:06:14 +0100 Subject: [PATCH 519/569] Adnuntius Bid Adapter: native added (#9330) * package lock fix. * Add dimensions to prebid. * Adnuntius Bid Adapter. Added native as a media type. --- modules/adnuntiusBidAdapter.js | 41 +++++- test/spec/modules/adnuntiusBidAdapter_spec.js | 122 ++++++++++++++++++ 2 files changed, 160 insertions(+), 3 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 0ae8411c073..5ad5436e732 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -1,5 +1,5 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; import { isStr, deepAccess, logInfo } from '../src/utils.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -8,6 +8,7 @@ const BIDDER_CODE = 'adnuntius'; const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i'; const GVLID = 855; const DEFAULT_VAST_VERSION = 'vast4' +const DEFAULT_NATIVE = 'native' const checkSegment = function (segment) { if (isStr(segment)) return segment; @@ -27,6 +28,32 @@ const getSegmentsFromOrtb = function (ortb2) { return segments } +function createNative(ad) { + const native = {}; + const assets = ad.assets + native.title = ad.text.title.content; + native.image = { + url: assets.image.cdnId, + height: assets.image.height, + width: assets.image.width, + }; + if (assets.icon) { + native.icon = { + url: assets.icon.cdnId, + height: assets.icon.height, + width: assets.icon.width, + }; + } + + native.sponsoredBy = ad.text.sponsoredBy?.content || ''; + native.body = ad.text.body?.content || ''; + native.cta = ad.text.cta?.content || ''; + native.clickUrl = ad.destinationUrls.destination || ''; + native.impressionTrackers = ad.impressionTrackingUrls || [ad.renderedPixel]; + + return native; +} + const handleMeta = function () { const storage = getStorageManager({ gvlid: GVLID, bidderCode: BIDDER_CODE }) let adnMeta = null @@ -46,7 +73,7 @@ const getUsi = function (meta, ortb2, bidderRequest) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [BANNER, VIDEO], + supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: function (bid) { return !!(bid.bidId || (bid.params.member && bid.params.invCode)); }, @@ -82,6 +109,10 @@ export const spec = { network += '_video' } + if (bid.mediaTypes && bid.mediaTypes.native) { + network += '_native' + } + bidRequests[network] = bidRequests[network] || []; bidRequests[network].push(bid); @@ -99,6 +130,7 @@ export const spec = { const network = networkKeys[j]; const networkRequest = [...request] if (network.indexOf('_video') > -1) { networkRequest.push('tt=' + DEFAULT_VAST_VERSION) } + if (network.indexOf('_native') > -1) { networkRequest.push('tt=' + DEFAULT_NATIVE) } requests.push({ method: 'POST', url: ENDPOINT_URL + '?' + networkRequest.join('&'), @@ -137,7 +169,10 @@ export const spec = { if (adUnit.vastXml) { adResponse[adUnit.targetId].vastXml = adUnit.vastXml - adResponse[adUnit.targetId].mediaType = 'video' + adResponse[adUnit.targetId].mediaType = VIDEO + } else if (ad.assets && ad.assets.image && ad.text && ad.text.title && ad.text.body && ad.destinationUrls && ad.destinationUrls.destination) { + adResponse[adUnit.targetId].native = createNative(ad); + adResponse[adUnit.targetId].mediaType = NATIVE; } else { adResponse[adUnit.targetId].ad = adUnit.html } diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 2d5ea630f0f..ee585862800 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -71,6 +71,30 @@ describe('adnuntiusBidAdapter', function () { } ] + const nativeBidderRequest = [ + { + bidId: '123', + bidder: 'adnuntius', + params: { + auId: '8b6bc', + network: 'adnuntius', + }, + mediaTypes: { + native: { + title: { + required: true + }, + image: { + required: true + }, + body: { + required: true + } + } + }, + } + ] + const singleBidRequest = { bid: [ { @@ -83,6 +107,10 @@ describe('adnuntiusBidAdapter', function () { bid: videoBidderRequest } + const nativeBidRequest = { + bid: nativeBidderRequest + } + const serverResponse = { body: { 'adUnits': [ @@ -209,6 +237,83 @@ describe('adnuntiusBidAdapter', function () { ] } } + const serverNativeResponse = { + body: { + 'adUnits': [ + { + 'auId': '000000000008b6bc', + 'targetId': '123', + 'html': '

hi!

', + 'matchedAdCount': 1, + 'responseId': 'adn-rsp-1460129238', + 'ads': [ + { + 'destinationUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com', + 'assets': { + 'image': { + 'cdnId': 'https://assets.adnuntius.com/K9rfXC6wJvgVuy4Fbt5P8oEEGXme9ZaP8BNDzz3OMGQ.jpg', + 'width': '300', + 'height': '250' + } + }, + 'text': { + 'body': { + 'content': 'Testing Native ad from Adnuntius', + 'length': '32', + 'minLength': '0', + 'maxLength': '100' + }, + 'title': { + 'content': 'Native Ad', + 'length': '9', + 'minLength': '5', + 'maxLength': '100' + } + }, + 'clickUrl': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'urls': { + 'destination': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN?ct=2501&r=http%3A%2F%2Fgoogle.com' + }, + 'urlsEsc': { + 'destination': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com' + }, + 'destinationUrls': { + 'destination': 'http://google.com' + }, + 'cpm': { 'amount': 5.0, 'currency': 'NOK' }, + 'bid': { 'amount': 0.005, 'currency': 'NOK' }, + 'cost': { 'amount': 0.005, 'currency': 'NOK' }, + 'impressionTrackingUrls': [], + 'impressionTrackingUrlsEsc': [], + 'adId': 'adn-id-1347343135', + 'selectedColumn': '0', + 'selectedColumnPosition': '0', + 'renderedPixel': 'https://delivery.adnuntius.com/b/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', + 'renderedPixelEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fb%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', + 'visibleUrl': 'https://delivery.adnuntius.com/s?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'visibleUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fs%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'viewUrl': 'https://delivery.adnuntius.com/v?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'viewUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fv%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'rt': '52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'creativeWidth': '980', + 'creativeHeight': '120', + 'creativeId': 'wgkq587vgtpchsx1', + 'lineItemId': 'scyjdyv3mzgdsnpf', + 'layoutId': 'sw6gtws2rdj1kwby', + 'layoutName': 'Responsive image' + }, + + ] + }, + { + 'auId': '000000000008b6bc', + 'targetId': '456', + 'matchedAdCount': 0, + 'responseId': 'adn-rsp-1460129238', + } + ] + } + } describe('inherited functions', function () { it('exists and is a function', function () { @@ -426,4 +531,21 @@ describe('adnuntiusBidAdapter', function () { expect(interpretedResponse[0].vastXml).to.equal(serverVideoResponse.body.adUnits[0].vastXml); }); }); + describe('interpretNativeResponse', function () { + it('should return valid response when passed valid server response', function () { + const interpretedResponse = spec.interpretResponse(serverNativeResponse, nativeBidRequest); + const ad = serverNativeResponse.body.adUnits[0].ads[0] + expect(interpretedResponse).to.have.lengthOf(1); + expect(interpretedResponse[0].cpm).to.equal(ad.cpm.amount); + expect(interpretedResponse[0].width).to.equal(Number(ad.creativeWidth)); + expect(interpretedResponse[0].height).to.equal(Number(ad.creativeHeight)); + expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); + expect(interpretedResponse[0].currency).to.equal(ad.bid.currency); + expect(interpretedResponse[0].netRevenue).to.equal(false); + expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[0].meta.advertiserDomains).to.have.lengthOf(1); + expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('google.com'); + expect(interpretedResponse[0].native.body).to.equal(serverNativeResponse.body.adUnits[0].ads[0].text.body.content); + }); + }); }); From d646c55ced9f8ee6f596df1cd717e72b1c0ebcd7 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 13 Dec 2022 12:53:41 -0700 Subject: [PATCH 520/569] Prebid core: enrich FPD by default (#9205) * Move rootDomain * Move FPD enrichments to core * Remove fpdEnrichments module * Cleanup * Add `site`, `device`, and coppa enrichments * FPD enrichments: GDPR * FPD enrichments: USP * Enrich FPD from requestBids * Fix typo in package.json --- libraries/ortbConverter/processors/default.js | 38 +-- modules/.submodules.json | 1 - modules/consentManagement.js | 22 +- modules/consentManagementUsp.js | 16 +- modules/enrichmentFpdModule.js | 192 +----------- modules/userId/index.js | 56 +--- src/fpd/enrichment.js | 94 ++++++ src/fpd/rootDomain.js | 57 ++++ src/prebid.js | 8 +- test/helpers/fpd.js | 71 +++++ test/spec/fpd/enrichment_spec.js | 213 +++++++++++++ test/spec/fpd/gdpr_spec.js | 47 +++ test/spec/fpd/rootDomain_spec.js | 61 ++++ test/spec/fpd/usp_spec.js | 36 +++ test/spec/modules/enrichmentFpdModule_spec.js | 159 ---------- test/spec/modules/fpdModule_spec.js | 288 +----------------- .../modules/improvedigitalBidAdapter_spec.js | 28 +- test/spec/modules/openxOrtbBidAdapter_spec.js | 35 ++- .../modules/prebidServerBidAdapter_spec.js | 76 +++-- test/spec/modules/userId_spec.js | 41 --- .../ortbConverter/default_processors_spec.js | 61 ---- test/spec/ortbConverter/gdpr_spec.js | 31 +- test/spec/ortbConverter/usp_spec.js | 15 - test/spec/unit/pbjs_api_spec.js | 70 +++-- 24 files changed, 751 insertions(+), 965 deletions(-) create mode 100644 src/fpd/enrichment.js create mode 100644 src/fpd/rootDomain.js create mode 100644 test/helpers/fpd.js create mode 100644 test/spec/fpd/enrichment_spec.js create mode 100644 test/spec/fpd/gdpr_spec.js create mode 100644 test/spec/fpd/rootDomain_spec.js create mode 100644 test/spec/fpd/usp_spec.js delete mode 100644 test/spec/ortbConverter/default_processors_spec.js delete mode 100644 test/spec/ortbConverter/usp_spec.js diff --git a/libraries/ortbConverter/processors/default.js b/libraries/ortbConverter/processors/default.js index 70acb7c9953..8d44de00fa2 100644 --- a/libraries/ortbConverter/processors/default.js +++ b/libraries/ortbConverter/processors/default.js @@ -1,4 +1,4 @@ -import {deepSetValue, getDefinedParams, getDNT, mergeDeep} from '../../../src/utils.js'; +import {deepSetValue, mergeDeep} from '../../../src/utils.js'; import {bannerResponseProcessor, fillBannerImp} from './banner.js'; import {fillVideoImp, fillVideoResponse} from './video.js'; import {setResponseMediaType} from './mediaType.js'; @@ -20,14 +20,6 @@ export const DEFAULT_PROCESSORS = { appFpd: fpdFromTopLevelConfig('app'), siteFpd: fpdFromTopLevelConfig('site'), deviceFpd: fpdFromTopLevelConfig('device'), - device: { - // sets device w / h / ua / language - fn: setDevice - }, - site: { - // sets site.domain, page, and ref from refererInfo - fn: setSite - }, props: { // sets request properties id, tmax, test, source.tid fn(ortbRequest, bidderRequest) { @@ -41,15 +33,7 @@ export const DEFAULT_PROCESSORS = { } deepSetValue(ortbRequest, 'source.tid', ortbRequest.source?.tid || bidderRequest.auctionId); } - }, - coppa: { - fn(ortbRequest) { - const coppa = config.getConfig('coppa'); - if (typeof coppa === 'boolean') { - deepSetValue(ortbRequest, 'regs.coppa', coppa ? 1 : 0); - } - } - }, + } }, [IMP]: { fpd: { @@ -144,24 +128,8 @@ function fpdFromTopLevelConfig(prop) { fn(ortbRequest) { const data = config.getConfig(prop); if (typeof data === 'object') { - ortbRequest[prop] = data; + ortbRequest[prop] = mergeDeep({}, ortbRequest[prop], data); } } } } - -export function setDevice(ortbRequest) { - ortbRequest.device = Object.assign({ - w: window.innerWidth, - h: window.innerHeight, - dnt: getDNT() ? 1 : 0, - ua: window.navigator.userAgent, - language: window.navigator.language.split('-').shift() - }, ortbRequest.device); -} - -export function setSite(ortbRequest, bidderRequest) { - if (bidderRequest.refererInfo) { - ortbRequest.site = Object.assign(getDefinedParams(bidderRequest.refererInfo, ['page', 'domain', 'ref']), ortbRequest.site); - } -} diff --git a/modules/.submodules.json b/modules/.submodules.json index f8c30459e1e..c3016914f1b 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -76,7 +76,6 @@ "zeusPrimeRtdProvider" ], "fpdModule": [ - "enrichmentFpdModule", "validationFpdModule", "topicsFpdModule" ], diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 95d622e55e4..6ca12010c74 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -10,6 +10,7 @@ import {gdprDataHandler} from '../src/adapterManager.js'; import {includes} from '../src/polyfill.js'; import {timedAuctionHook} from '../src/utils/perfMetrics.js'; import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; +import {enrichFPD} from '../src/fpd/enrichment.js'; const DEFAULT_CMP = 'iab'; const DEFAULT_CONSENT_TIMEOUT = 10000; @@ -356,22 +357,27 @@ export function setConsentConfig(config) { } config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement)); -export function setOrtbGdpr(ortbRequest, bidderRequest) { - const consent = bidderRequest.gdprConsent; - if (consent) { - if (typeof consent.gdprApplies === 'boolean') { - deepSetValue(ortbRequest, 'regs.ext.gdpr', consent.gdprApplies ? 1 : 0); +export function enrichFPDHook(next, fpd) { + return next(fpd.then(ortb2 => { + const consent = gdprDataHandler.getConsentData(); + if (consent) { + if (typeof consent.gdprApplies === 'boolean') { + deepSetValue(ortb2, 'regs.ext.gdpr', consent.gdprApplies ? 1 : 0); + } + deepSetValue(ortb2, 'user.ext.consent', consent.consentString); } - deepSetValue(ortbRequest, 'user.ext.consent', consent.consentString); - } + return ortb2; + })); } +enrichFPD.before(enrichFPDHook); + export function setOrtbAdditionalConsent(ortbRequest, bidderRequest) { + // this is not a standardized name for addtlConsent, so keep this as an ORTB library processor rather than an FPD enrichment const addtl = bidderRequest.gdprConsent?.addtlConsent; if (addtl && typeof addtl === 'string') { deepSetValue(ortbRequest, 'user.ext.ConsentedProvidersSettings.consented_providers', addtl); } } -registerOrtbProcessor({type: REQUEST, name: 'gdpr', fn: setOrtbGdpr}); registerOrtbProcessor({type: REQUEST, name: 'gdprAddtlConsent', fn: setOrtbAdditionalConsent}) diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index 805c796312c..0a99ec4a913 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -7,9 +7,9 @@ import {deepSetValue, isFn, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js'; import {config} from '../src/config.js'; import adapterManager, {uspDataHandler} from '../src/adapterManager.js'; -import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; import {timedAuctionHook} from '../src/utils/perfMetrics.js'; import {getHook} from '../src/hook.js'; +import {enrichFPD} from '../src/fpd/enrichment.js'; const DEFAULT_CONSENT_API = 'iab'; const DEFAULT_CONSENT_TIMEOUT = 50; @@ -323,10 +323,14 @@ config.getConfig('consentManagement', config => setConsentConfig(config.consentM getHook('requestBids').before(requestBidsHook, 50); -export function setOrtbUsp(ortbRequest, bidderRequest) { - if (bidderRequest.uspConsent) { - deepSetValue(ortbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); - } +export function enrichFPDHook(next, fpd) { + return next(fpd.then(ortb2 => { + const consent = uspDataHandler.getConsentData(); + if (consent) { + deepSetValue(ortb2, 'regs.ext.us_privacy', consent) + } + return ortb2; + })) } -registerOrtbProcessor({type: REQUEST, name: 'usp', fn: setOrtbUsp}); +enrichFPD.before(enrichFPDHook); diff --git a/modules/enrichmentFpdModule.js b/modules/enrichmentFpdModule.js index a4d7c06d0fb..59d5d326109 100644 --- a/modules/enrichmentFpdModule.js +++ b/modules/enrichmentFpdModule.js @@ -1,190 +1,2 @@ - -/** - * This module sets default values and validates ortb2 first part data - * @module modules/firstPartyData - */ -import { timestamp, mergeDeep } from '../src/utils.js'; -import { submodule } from '../src/hook.js'; -import {getRefererInfo, parseDomain} from '../src/refererDetection.js'; -import { getCoreStorageManager } from '../src/storageManager.js'; -import {GreedyPromise} from '../src/utils/promise.js'; -import {getHighEntropySUA, getLowEntropySUA} from '../libraries/fpd/sua.js'; - -let ortb2; -let win = (window === window.top) ? window : window.top; -export const coreStorage = getCoreStorageManager('enrichmentFpd'); - -export const sua = {he: getHighEntropySUA, le: getLowEntropySUA}; - -/** - * Find the root domain - * @param {string|undefined} fullDomain - * @return {string} -*/ -export function findRootDomain(fullDomain = window.location.hostname) { - if (!coreStorage.cookiesAreEnabled()) { - return fullDomain; - } - - const domainParts = fullDomain.split('.'); - if (domainParts.length == 2) { - return fullDomain; - } - let rootDomain; - let continueSearching; - let startIndex = -2; - const TEST_COOKIE_NAME = `_rdc${Date.now()}`; - const TEST_COOKIE_VALUE = 'writeable'; - do { - rootDomain = domainParts.slice(startIndex).join('.'); - let expirationDate = new Date(timestamp() + 10 * 1000).toUTCString(); - - // Write a test cookie - coreStorage.setCookie( - TEST_COOKIE_NAME, - TEST_COOKIE_VALUE, - expirationDate, - 'Lax', - rootDomain, - undefined - ); - - // See if the write was successful - const value = coreStorage.getCookie(TEST_COOKIE_NAME, undefined); - if (value === TEST_COOKIE_VALUE) { - continueSearching = false; - // Delete our test cookie - coreStorage.setCookie( - TEST_COOKIE_NAME, - '', - 'Thu, 01 Jan 1970 00:00:01 GMT', - undefined, - rootDomain, - undefined - ); - } else { - startIndex += -1; - continueSearching = Math.abs(startIndex) <= domainParts.length; - } - } while (continueSearching); - return rootDomain; -} - -/** - * Checks for referer and if exists merges into ortb2 global data - */ -function setReferer() { - if (getRefererInfo().ref) mergeDeep(ortb2, { site: { ref: getRefererInfo().ref } }); -} - -/** - * Checks for canonical url and if exists merges into ortb2 global data - */ -function setPage() { - if (getRefererInfo().page) mergeDeep(ortb2, { site: { page: getRefererInfo().page } }); -} - -/** - * Checks for canonical url and if exists retrieves domain and merges into ortb2 global data - */ -function setDomain() { - const domain = parseDomain(getRefererInfo().page, {noLeadingWww: true}); - if (domain) { - mergeDeep(ortb2, { site: { domain: domain } }); - mergeDeep(ortb2, { site: { publisher: { domain: findRootDomain(domain) } } }); - }; -} - -/** - * Checks for screen/device width and height and sets dimensions - */ -function setDimensions() { - let width; - let height; - - try { - width = win.innerWidth || win.document.documentElement.clientWidth || win.document.body.clientWidth; - height = win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight; - } catch (e) { - width = window.innerWidth || window.document.documentElement.clientWidth || window.document.body.clientWidth; - height = window.innerHeight || window.document.documentElement.clientHeight || window.document.body.clientHeight; - } - - mergeDeep(ortb2, { device: { w: width, h: height } }); -} - -/** - * Scans page for meta keywords, and if exists, merges into site.keywords - */ -function setKeywords() { - let keywords; - - try { - keywords = win.document.querySelector("meta[name='keywords']"); - } catch (e) { - keywords = window.document.querySelector("meta[name='keywords']"); - } - - if (keywords && keywords.content) mergeDeep(ortb2, { site: { keywords: keywords.content.replace(/\s/g, '') } }); -} - -function setDeviceSua(hints) { - let data = Array.isArray(hints) && hints.length === 0 - ? GreedyPromise.resolve(sua.le()) - : sua.he(hints); - return data.then((sua) => { - if (sua != null) { - mergeDeep(ortb2, {device: {sua}}); - } - }) -} - -/** - * Checks the Global Privacy Control status, and if exists and is true, merges into regs.ext.gpc - */ -function setGpc() { - const gpcValue = navigator.globalPrivacyControl; - if (gpcValue) { - mergeDeep(ortb2, { regs: { ext: { gpc: 1 } } }) - } -} - -/** - * Resets modules global ortb2 data - */ -export const resetEnrichments = () => { ortb2 = null }; - -function runEnrichments(fpdConf) { - setReferer(); - setPage(); - setDomain(); - setDimensions(); - setKeywords(); - setGpc(); - return setDeviceSua(fpdConf.uaHints).then(() => ortb2); -} - -export function processFpd(fpdConf, {global}) { - if (fpdConf.skipEnrichments) { - return {global}; - } else { - let ready; - if (ortb2 == null) { - ortb2 = {}; - ready = runEnrichments(fpdConf); - } else { - ready = GreedyPromise.resolve(); - } - return ready.then(() => ({ - global: mergeDeep({}, ortb2, global) - })) - } -} -/** @type {firstPartyDataSubmodule} */ -export const enrichmentsSubmodule = { - name: 'enrichments', - queue: 2, - processFpd -} - -submodule('firstPartyData', enrichmentsSubmodule) +// Logic from this module was moved into core since approx. 7.27 +// TODO: remove this in v8 diff --git a/modules/userId/index.js b/modules/userId/index.js index fe47ac5c16d..82ebaf2b14e 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -148,7 +148,6 @@ import { logError, logInfo, logWarn, - timestamp, isEmpty, deepSetValue } from '../../src/utils.js'; import {getPPID as coreGetPPID} from '../../src/adserver.js'; @@ -156,6 +155,7 @@ import {defer, GreedyPromise} from '../../src/utils/promise.js'; import {hasPurpose1Consent} from '../../src/utils/gpdr.js'; import {registerOrtbProcessor, REQUEST} from '../../src/pbjsORTB.js'; import {newMetrics, timedAuctionHook, useMetrics} from '../../src/utils/perfMetrics.js'; +import {findRootDomain} from '../../src/fpd/rootDomain.js'; const MODULE_NAME = 'User ID'; const COOKIE = 'cookie'; @@ -385,60 +385,6 @@ function storedConsentDataMatchesConsentData(storedConsentData, consentData) { ); } -/** - * Find the root domain - * @param {string|undefined} fullDomain - * @return {string} - */ -export function findRootDomain(fullDomain = window.location.hostname) { - if (!coreStorage.cookiesAreEnabled()) { - return fullDomain; - } - - const domainParts = fullDomain.split('.'); - if (domainParts.length == 2) { - return fullDomain; - } - let rootDomain; - let continueSearching; - let startIndex = -2; - const TEST_COOKIE_NAME = `_rdc${Date.now()}`; - const TEST_COOKIE_VALUE = 'writeable'; - do { - rootDomain = domainParts.slice(startIndex).join('.'); - let expirationDate = new Date(timestamp() + 10 * 1000).toUTCString(); - - // Write a test cookie - coreStorage.setCookie( - TEST_COOKIE_NAME, - TEST_COOKIE_VALUE, - expirationDate, - 'Lax', - rootDomain, - undefined - ); - - // See if the write was successful - const value = coreStorage.getCookie(TEST_COOKIE_NAME, undefined); - if (value === TEST_COOKIE_VALUE) { - continueSearching = false; - // Delete our test cookie - coreStorage.setCookie( - TEST_COOKIE_NAME, - '', - 'Thu, 01 Jan 1970 00:00:01 GMT', - undefined, - rootDomain, - undefined - ); - } else { - startIndex += -1; - continueSearching = Math.abs(startIndex) <= domainParts.length; - } - } while (continueSearching); - return rootDomain; -} - /** * @param {SubmoduleContainer[]} submodules * @param {function} cb - callback for after processing is done. diff --git a/src/fpd/enrichment.js b/src/fpd/enrichment.js new file mode 100644 index 00000000000..fc50a057378 --- /dev/null +++ b/src/fpd/enrichment.js @@ -0,0 +1,94 @@ +import {hook} from '../hook.js'; +import {getRefererInfo, parseDomain} from '../refererDetection.js'; +import {findRootDomain} from './rootDomain.js'; +import {deepSetValue, getDefinedParams, getDNT, getWindowSelf, getWindowTop, mergeDeep} from '../utils.js'; +import {config} from '../config.js'; +import {getHighEntropySUA, getLowEntropySUA} from '../../libraries/fpd/sua.js'; +import {GreedyPromise} from '../utils/promise.js'; + +export const dep = { + getRefererInfo, + findRootDomain, + getWindowTop, + getWindowSelf, + getHighEntropySUA, + getLowEntropySUA, +}; + +/** + * Enrich an ortb2 object with first party data. + * @param {Promise[{}]} fpd: a promise to an ortb2 object. + * @returns: {Promise[{}]}: a promise to an enriched ortb2 object. + */ +export const enrichFPD = hook('sync', (fpd) => { + return GreedyPromise.all([fpd, getSUA().catch(() => null)]) + .then(([ortb2, sua]) => { + Object.entries(ENRICHMENTS).forEach(([section, getEnrichments]) => { + const data = getEnrichments(); + if (data && Object.keys(data).length > 0) { + ortb2[section] = mergeDeep({}, data, ortb2[section]); + } + }); + if (sua) { + deepSetValue(ortb2, 'device.sua', Object.assign({}, sua, ortb2.device.sua)); + } + return ortb2; + }); +}); + +function winFallback(fn) { + try { + return fn(dep.getWindowTop()); + } catch (e) { + return fn(dep.getWindowSelf()); + } +} + +function getSUA() { + const hints = config.getConfig('firstPartyData.uaHints'); + return Array.isArray(hints) && hints.length === 0 + ? GreedyPromise.resolve(dep.getLowEntropySUA()) + : dep.getHighEntropySUA(hints); +} + +const ENRICHMENTS = { + site() { + const ri = dep.getRefererInfo(); + const domain = parseDomain(ri.page, {noLeadingWww: true}); + const keywords = winFallback((win) => win.document.querySelector('meta[name=\'keywords\']')) + ?.content?.replace?.(/\s/g, ''); + return (site => getDefinedParams(site, Object.keys(site)))({ + page: ri.page, + ref: ri.ref, + domain, + keywords, + publisher: { + domain: dep.findRootDomain(domain) + } + }); + }, + device() { + return winFallback((win) => { + const w = win.innerWidth || win.document.documentElement.clientWidth || win.document.body.clientWidth; + const h = win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight; + return { + w, + h, + dnt: getDNT() ? 1 : 0, + ua: win.navigator.userAgent, + language: win.navigator.language.split('-').shift(), + }; + }) + }, + regs() { + const regs = {}; + if (winFallback((win) => win.navigator.globalPrivacyControl)) { + deepSetValue(regs, 'ext.gpc', 1); + } + const coppa = config.getConfig('coppa'); + if (typeof coppa === 'boolean') { + regs.coppa = coppa ? 1 : 0; + } + return regs; + } +}; diff --git a/src/fpd/rootDomain.js b/src/fpd/rootDomain.js new file mode 100644 index 00000000000..4095613672f --- /dev/null +++ b/src/fpd/rootDomain.js @@ -0,0 +1,57 @@ +import {memoize, timestamp} from '../utils.js'; +import {getCoreStorageManager} from '../storageManager.js'; + +export const coreStorage = getCoreStorageManager(); + +/** + * Find the root domain by testing for the topmost domain that will allow setting cookies. + */ + +export const findRootDomain = memoize(function findRootDomain(fullDomain = window.location.host) { + if (!coreStorage.cookiesAreEnabled()) { + return fullDomain; + } + + const domainParts = fullDomain.split('.'); + if (domainParts.length === 2) { + return fullDomain; + } + let rootDomain; + let continueSearching; + let startIndex = -2; + const TEST_COOKIE_NAME = `_rdc${Date.now()}`; + const TEST_COOKIE_VALUE = 'writeable'; + do { + rootDomain = domainParts.slice(startIndex).join('.'); + let expirationDate = new Date(timestamp() + 10 * 1000).toUTCString(); + + // Write a test cookie + coreStorage.setCookie( + TEST_COOKIE_NAME, + TEST_COOKIE_VALUE, + expirationDate, + 'Lax', + rootDomain, + undefined + ); + + // See if the write was successful + const value = coreStorage.getCookie(TEST_COOKIE_NAME, undefined); + if (value === TEST_COOKIE_VALUE) { + continueSearching = false; + // Delete our test cookie + coreStorage.setCookie( + TEST_COOKIE_NAME, + '', + 'Thu, 01 Jan 1970 00:00:01 GMT', + undefined, + rootDomain, + undefined + ); + } else { + startIndex += -1; + continueSearching = Math.abs(startIndex) <= domainParts.length; + } + } while (continueSearching); + return rootDomain; +}); diff --git a/src/prebid.js b/src/prebid.js index 06429b13a72..f30b4ea9bbe 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -49,7 +49,8 @@ import {default as adapterManager, gdprDataHandler, getS2SBidderSet, uspDataHand import CONSTANTS from './constants.json'; import * as events from './events.js'; import {newMetrics, useMetrics} from './utils/perfMetrics.js'; -import {defer} from './utils/promise.js'; +import {defer, GreedyPromise} from './utils/promise.js'; +import {enrichFPD} from './fpd/enrichment.js'; const $$PREBID_GLOBAL$$ = getGlobal(); const { triggerUserSyncs } = userSync; @@ -631,7 +632,10 @@ $$PREBID_GLOBAL$$.requestBids = (function() { global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, cfg.ortb2]).filter(([_, ortb2]) => ortb2 != null)) } - return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2Fragments, metrics, defer}); + return enrichFPD(GreedyPromise.resolve(ortb2Fragments.global)).then(global => { + ortb2Fragments.global = global; + return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2Fragments, metrics, defer}); + }) }, 'requestBids'); return wrapHook(delegate, function requestBids(req = {}) { diff --git a/test/helpers/fpd.js b/test/helpers/fpd.js new file mode 100644 index 00000000000..89755f26541 --- /dev/null +++ b/test/helpers/fpd.js @@ -0,0 +1,71 @@ +import {dep, enrichFPD} from 'src/fpd/enrichment.js'; +import {GreedyPromise} from '../../src/utils/promise.js'; +import {deepClone} from '../../src/utils.js'; +import {gdprDataHandler, uspDataHandler} from '../../src/adapterManager.js'; + +export function mockFpdEnrichments(sandbox, overrides = {}) { + overrides = Object.assign({}, { + // override window getters, required for ChromeHeadless, apparently it sees window.self !== window + getWindowTop() { + return window + }, + getWindowSelf() { + return window + }, + getHighEntropySUA() { + return GreedyPromise.resolve() + } + }, overrides) + Object.entries(overrides) + .filter(([k]) => dep[k]) + .forEach(([k, v]) => { + sandbox.stub(dep, k).callsFake(v); + }); + Object.entries({ + gdprConsent: gdprDataHandler, + uspConsent: uspDataHandler, + }).forEach(([ovKey, handler]) => { + const v = overrides[ovKey]; + if (v) { + sandbox.stub(handler, 'getConsentData').callsFake(v); + } + }) +} + +export function addFPDEnrichments(ortb2 = {}, overrides) { + const sandbox = sinon.sandbox.create(); + mockFpdEnrichments(sandbox, overrides) + return enrichFPD(GreedyPromise.resolve(deepClone(ortb2))).finally(() => sandbox.restore()); +} + +export const syncAddFPDEnrichments = synchronize(addFPDEnrichments); + +export function addFPDToBidderRequest(bidderRequest, overrides) { + overrides = Object.assign({}, { + getRefererInfo() { + return bidderRequest.refererInfo || {}; + }, + gdprConsent() { + return bidderRequest.gdprConsent; + }, + uspConsent() { + return bidderRequest.uspConsent; + } + }, overrides); + return addFPDEnrichments(bidderRequest.ortb2 || {}, overrides).then(ortb2 => { + return { + ...bidderRequest, + ortb2 + } + }); +} + +export const syncAddFPDToBidderRequest = synchronize(addFPDToBidderRequest); + +function synchronize(fn) { + return function () { + let result; + fn.apply(this, arguments).then(res => { result = res }); + return result; + } +} diff --git a/test/spec/fpd/enrichment_spec.js b/test/spec/fpd/enrichment_spec.js new file mode 100644 index 00000000000..3363e1867d3 --- /dev/null +++ b/test/spec/fpd/enrichment_spec.js @@ -0,0 +1,213 @@ +import {dep, enrichFPD} from '../../../src/fpd/enrichment.js'; +import {hook} from '../../../src/hook.js'; +import {expect} from 'chai/index.mjs'; +import {config} from 'src/config.js'; + +describe('FPD enrichment', () => { + let sandbox; + before(() => { + hook.ready(); + }); + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + afterEach(() => { + sandbox.restore(); + config.resetConfig(); + }); + + function fpd(ortb2 = {}) { + return enrichFPD(Promise.resolve(ortb2)); + } + + function mockWindow() { + return { + innerHeight: 1, + innerWidth: 1, + navigator: { + language: '' + }, + document: { + querySelector: sinon.stub() + } + }; + } + + function testWindows(getWindow, fn) { + Object.entries({ + 'top': ['getWindowTop', 'getWindowSelf'], + 'self': ['getWindowSelf', 'getWindowTop'] + }).forEach(([t, [winWorks, winThrows]]) => { + describe(`using window.${t}`, () => { + beforeEach(() => { + sandbox.stub(dep, winWorks).callsFake(getWindow); + sandbox.stub(dep, winThrows).throws(new Error()); + }); + fn(); + }); + }); + } + + describe('site', () => { + it('sets page, ref, domain, and publisher.domain', () => { + const refererInfo = { + page: 'www.example.com', + ref: 'referrer.com' + }; + sandbox.stub(dep, 'getRefererInfo').callsFake(() => refererInfo); + sandbox.stub(dep, 'findRootDomain').callsFake((dom) => `publisher.${dom}`); + return fpd().then(ortb2 => { + sinon.assert.match(ortb2.site, { + page: 'www.example.com', + domain: 'example.com', + ref: 'referrer.com', + publisher: { + domain: 'publisher.example.com' + } + }); + }); + }); + + describe('keywords', () => { + let metaTag; + beforeEach(() => { + metaTag = document.createElement('meta'); + metaTag.name = 'keywords'; + metaTag.content = 'kw1, kw2'; + document.head.appendChild(metaTag); + }); + afterEach(() => { + document.head.removeChild(metaTag); + }); + + testWindows(() => window, () => { + it(`sets kewwords from meta tag`, () => { + return fpd().then(ortb2 => { + expect(ortb2.site.keywords).to.eql('kw1,kw2'); + }); + }); + }); + }); + + it('should not set keywords if meta tag is not present', () => { + return fpd().then(ortb2 => { + expect(ortb2.site.hasOwnProperty('keywords')).to.be.false; + }); + }); + + it('respects pub-provided fpd', () => { + return fpd({ + site: { + publisher: { + domain: 'pub.com' + } + } + }).then(ortb2 => { + expect(ortb2.site.publisher.domain).to.eql('pub.com'); + }); + }); + }); + + describe('device', () => { + let win; + beforeEach(() => { + win = mockWindow(); + }); + testWindows(() => win, () => { + it('sets w/h', () => { + win.innerHeight = 123; + win.innerWidth = 321; + return fpd().then(ortb2 => { + sinon.assert.match(ortb2.device, { + w: 321, + h: 123, + }); + }); + }); + + it('sets ua', () => { + win.navigator.userAgent = 'mock-ua'; + return fpd().then(ortb2 => { + expect(ortb2.device.ua).to.eql('mock-ua'); + }) + }); + + it('sets language', () => { + win.navigator.language = 'lang-ignored'; + return fpd().then(ortb2 => { + expect(ortb2.device.language).to.eql('lang'); + }) + }); + }); + }); + + describe('regs', () => { + describe('gpc', () => { + let win; + beforeEach(() => { + win = mockWindow(); + }); + testWindows(() => win, () => { + it('is set if globalPrivacyControl is set', () => { + win.navigator.globalPrivacyControl = true; + return fpd().then(ortb2 => { + expect(ortb2.regs.ext.gpc).to.eql(1); + }); + }); + + it('is not set otherwise', () => { + return fpd().then(ortb2 => { + expect(ortb2.regs?.ext?.gpc).to.not.exist; + }) + }) + }); + }) + describe('coppa', () => { + [[true, 1], [false, 0]].forEach(([cfgVal, regVal]) => { + it(`is set to ${regVal} if config = ${cfgVal}`, () => { + config.setConfig({coppa: cfgVal}); + return fpd().then(ortb2 => { + expect(ortb2.regs.coppa).to.eql(regVal); + }) + }); + }) + + it('is not set if not configured', () => { + return fpd().then(ortb2 => { + expect(ortb2.regs?.coppa).to.not.exist; + }) + }) + }); + }); + + describe('sua', () => { + it('does not set device.sua if resolved sua is null', () => { + sandbox.stub(dep, 'getHighEntropySUA').returns(Promise.resolve()) + return fpd().then(ortb2 => { + expect(ortb2.device.sua).to.not.exist; + }) + }); + it('uses low entropy values if uaHints is []', () => { + sandbox.stub(dep, 'getLowEntropySUA').callsFake(() => ({mock: 'sua'})); + config.setConfig({ + firstPartyData: { + uaHints: [], + } + }) + return fpd().then(ortb2 => { + expect(ortb2.device.sua).to.eql({mock: 'sua'}); + }) + }); + it('uses high entropy values otherwise', () => { + sandbox.stub(dep, 'getHighEntropySUA').callsFake((hints) => Promise.resolve({hints})); + config.setConfig({ + firstPartyData: { + uaHints: ['h1', 'h2'] + } + }); + return fpd().then(ortb2 => { + expect(ortb2.device.sua).to.eql({hints: ['h1', 'h2']}) + }) + }); + }); +}); diff --git a/test/spec/fpd/gdpr_spec.js b/test/spec/fpd/gdpr_spec.js new file mode 100644 index 00000000000..8fc04815112 --- /dev/null +++ b/test/spec/fpd/gdpr_spec.js @@ -0,0 +1,47 @@ +import {gdprDataHandler} from '../../../src/adapterManager.js'; +import {enrichFPDHook} from '../../../modules/consentManagement.js'; + +describe('GDPR FPD enrichment', () => { + let sandbox, consent; + beforeEach(() => { + consent = null; + sandbox = sinon.sandbox.create(); + sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => consent); + }); + afterEach(() => { + sandbox.restore(); + }) + + function callHook() { + let result; + enrichFPDHook((res) => { result = res }, Promise.resolve({})); + return result; + } + + it('sets gdpr properties from gdprDataHandler', () => { + consent = {gdprApplies: true, consentString: 'consent'}; + return callHook().then(ortb2 => { + expect(ortb2.regs.ext.gdpr).to.eql(1); + expect(ortb2.user.ext.consent).to.eql('consent'); + }) + }); + + it('does not set it if missing', () => { + return callHook().then((ortb2) => { + expect(ortb2).to.eql({}); + }) + }); + + it('sets user.ext.consent, but not regs.ext.gdpr, if gpdrApplies is not a boolean', () => { + consent = {consentString: 'mock-consent'}; + return callHook().then(ortb2 => { + expect(ortb2).to.eql({ + user: { + ext: { + consent: 'mock-consent' + } + } + }) + }) + }); +}); diff --git a/test/spec/fpd/rootDomain_spec.js b/test/spec/fpd/rootDomain_spec.js new file mode 100644 index 00000000000..008ef749edc --- /dev/null +++ b/test/spec/fpd/rootDomain_spec.js @@ -0,0 +1,61 @@ +import {expect} from 'chai/index.js'; +import {findRootDomain, coreStorage} from 'src/fpd/rootDomain.js'; + +describe('findRootDomain', function () { + let sandbox, cookies, rejectDomain; + + beforeEach(function () { + findRootDomain.clear(); + cookies = {}; + rejectDomain = ''; + sandbox = sinon.createSandbox(); + sandbox.stub(coreStorage, 'cookiesAreEnabled').returns(true); + sandbox.stub(coreStorage, 'setCookie').callsFake((cookie, value, expiration, sameSite, domain) => { + if (rejectDomain !== domain) { + if (new Date(expiration) <= Date.now()) { + delete cookies[cookie]; + } else { + cookies[cookie] = value; + } + } + }) + sandbox.stub(coreStorage, 'getCookie').callsFake((cookie) => { + return cookies[cookie] + }); + }); + + afterEach(function () { + sandbox.restore(); + }); + + after(() => { + findRootDomain.clear(); + }) + + it('should just find the root domain', function () { + rejectDomain = 'co.uk'; + var domain = findRootDomain('sub.realdomain.co.uk'); + expect(domain).to.be.eq('realdomain.co.uk'); + expect(cookies).to.eql({}); + }); + + it('should find the full domain when no subdomain is present', function () { + rejectDomain = 'co.uk'; + var domain = findRootDomain('realdomain.co.uk'); + expect(domain).to.be.eq('realdomain.co.uk'); + expect(cookies).to.eql({}); + }); + + it('should return domain as-is if cookies are disabled', () => { + coreStorage.cookiesAreEnabled.returns(false); + expect(findRootDomain('sub.example.com')).to.eql('sub.example.com'); + sinon.assert.notCalled(coreStorage.setCookie); + }); + + it('should memoize default value', () => { + const domain = findRootDomain(); + expect(domain.length > 0).to.be.true; + expect(findRootDomain()).to.eql(domain); + sinon.assert.calledOnce(coreStorage.getCookie); + }); +}); diff --git a/test/spec/fpd/usp_spec.js b/test/spec/fpd/usp_spec.js new file mode 100644 index 00000000000..f616b086ffa --- /dev/null +++ b/test/spec/fpd/usp_spec.js @@ -0,0 +1,36 @@ +import {enrichFPDHook} from '../../../modules/consentManagementUsp.js'; +import {uspDataHandler} from '../../../src/adapterManager.js'; + +describe('FPD enrichment USP', () => { + let sandbox, consent; + beforeEach(() => { + consent = null; + sandbox = sinon.sandbox.create(); + sandbox.stub(uspDataHandler, 'getConsentData').callsFake(() => consent); + }); + + afterEach(() => { + sandbox.restore(); + }); + + function callHook() { + let result; + enrichFPDHook((res) => { + result = res; + }, Promise.resolve({})); + return result; + } + + it('sets regs.ext.us_privacy from uspDataHandler', () => { + consent = '1NN'; + return callHook().then(ortb2 => { + expect(ortb2.regs.ext.us_privacy).to.eql('1NN'); + }) + }); + + it('does not set if missing', () => { + return callHook().then(ortb2 => { + expect(ortb2).to.eql({}); + }) + }); +}); diff --git a/test/spec/modules/enrichmentFpdModule_spec.js b/test/spec/modules/enrichmentFpdModule_spec.js index 38853f1528c..e69de29bb2d 100644 --- a/test/spec/modules/enrichmentFpdModule_spec.js +++ b/test/spec/modules/enrichmentFpdModule_spec.js @@ -1,159 +0,0 @@ -import { expect } from 'chai'; -import {config} from 'src/config.js'; -import { getRefererInfo } from 'src/refererDetection.js'; -import {processFpd, coreStorage, resetEnrichments} from 'modules/enrichmentFpdModule.js'; -import * as enrichmentModule from 'modules/enrichmentFpdModule.js'; -import {GreedyPromise} from '../../../src/utils/promise.js'; - -describe('the first party data enrichment module', function() { - let width; - let widthStub; - let height; - let heightStub; - let querySelectorStub; - let coreStorageStub; - let canonical; - let keywords; - let lowEntropySuaStub; - let highEntropySuaStub; - - before(function() { - canonical = document.createElement('link'); - canonical.rel = 'canonical'; - keywords = document.createElement('meta'); - keywords.name = 'keywords'; - }); - - beforeEach(function() { - resetEnrichments(); - querySelectorStub = sinon.stub(window.top.document, 'querySelector'); - querySelectorStub.withArgs("link[rel='canonical']").returns(canonical); - querySelectorStub.withArgs("meta[name='keywords']").returns(keywords); - widthStub = sinon.stub(window.top, 'innerWidth').get(function() { - return width; - }); - heightStub = sinon.stub(window.top, 'innerHeight').get(function() { - return height; - }); - coreStorageStub = sinon.stub(coreStorage, 'getCookie'); - coreStorageStub - .onFirstCall() - .returns(null) // co.uk - .onSecondCall() - .returns('writeable'); // domain.co.uk - lowEntropySuaStub = sinon.stub(enrichmentModule.sua, 'le').callsFake(() => null); - highEntropySuaStub = sinon.stub(enrichmentModule.sua, 'he').callsFake(() => GreedyPromise.resolve()); - }); - - afterEach(function() { - widthStub.restore(); - heightStub.restore(); - querySelectorStub.restore(); - coreStorageStub.restore(); - canonical = document.createElement('link'); - canonical.rel = 'canonical'; - keywords = document.createElement('meta'); - keywords.name = 'keywords'; - lowEntropySuaStub.restore(); - highEntropySuaStub.restore(); - }); - - function syncProcessFpd(fpdConf, ortb2Fragments) { - let result; - processFpd(fpdConf, ortb2Fragments).then((res) => { result = res; }); - return result; - }; - - it('adds ref and device values', function() { - width = 800; - height = 500; - - let validated = syncProcessFpd({}, {}).global; - - const {ref, page, domain} = getRefererInfo(); - expect(validated.site.ref).to.equal(ref || undefined); - expect(validated.site.page).to.equal(page || undefined) - expect(validated.site.domain).to.equal(domain || undefined) - expect(validated.device).to.deep.equal({ w: 800, h: 500 }); - expect(validated.site.keywords).to.be.undefined; - }); - - it('adds page domain values if pageUrl url exists', function() { - config.setConfig({'pageUrl': 'https://www.subdomain.domain.co.uk/path?query=12345'}); - width = 800; - height = 500; - - let validated = syncProcessFpd({}, {}).global; - - expect(validated.site.ref).to.equal(getRefererInfo().ref || undefined); - expect(validated.site.page).to.equal('https://www.subdomain.domain.co.uk/path?query=12345'); - expect(validated.site.domain).to.equal('subdomain.domain.co.uk'); - expect(validated.site.publisher.domain).to.equal('domain.co.uk'); - expect(validated.device).to.deep.equal({ w: 800, h: 500 }); - expect(validated.site.keywords).to.be.undefined; - }); - - it('adds keyword value if keyword meta content exists', function() { - width = 800; - height = 500; - keywords.content = 'value1,value2,value3'; - - let validated = syncProcessFpd({}, {}).global; - - expect(validated.site.keywords).to.equal('value1,value2,value3'); - }); - - it('does not overwrite existing data from getConfig ortb2', function() { - width = 800; - height = 500; - - let validated = syncProcessFpd({}, {global: {device: {w: 1200, h: 700}, site: {ref: 'https://someUrl.com', page: 'test.com'}}}).global; - - expect(validated.site.ref).to.equal('https://someUrl.com'); - expect(validated.site.page).to.equal('test.com'); - expect(validated.device).to.deep.equal({ w: 1200, h: 700 }); - expect(validated.site.keywords).to.be.undefined; - }); - - it('does not run enrichments again on the second call', () => { - width = 1; - height = 2; - const first = syncProcessFpd({}, {}).global; - width = 3; - const second = syncProcessFpd({}, {}).global; - expect(first).to.eql(second); - }); - - describe('device.sua', () => { - it('does not set device.sua if resolved sua is null', () => { - const sua = syncProcessFpd({}, {}).global.device?.sua; - expect(sua).to.not.exist; - }); - it('uses low entropy values if uaHints is []', () => { - lowEntropySuaStub.callsFake(() => ({mock: 'sua'})); - const sua = syncProcessFpd({uaHints: []}, {}).global.device.sua; - expect(sua).to.eql({mock: 'sua'}); - }); - it('uses high entropy values otherwise', () => { - highEntropySuaStub.callsFake((hints) => GreedyPromise.resolve({hints})); - const sua = syncProcessFpd({uaHints: ['h1', 'h2']}, {}).global.device.sua; - expect(sua).to.eql({hints: ['h1', 'h2']}); - }) - }) - - it('should store a reference to gpc witin ortb2.regs.ext if it has been enabled', function() { - let validated; - width = 800; - height = 500; - - validated = syncProcessFpd({}, {}).global; - expect(validated.regs).to.equal(undefined); - - resetEnrichments(); - - const globalPrivacyControlStub = sinon.stub(window, 'navigator').value({globalPrivacyControl: true}); - validated = syncProcessFpd({}, {}).global; - expect(validated.regs.ext.gpc).to.equal(1); - globalPrivacyControlStub.restore(); - }); -}); diff --git a/test/spec/modules/fpdModule_spec.js b/test/spec/modules/fpdModule_spec.js index 7d41fbb55e1..4536b304f9d 100644 --- a/test/spec/modules/fpdModule_spec.js +++ b/test/spec/modules/fpdModule_spec.js @@ -1,16 +1,14 @@ import {expect} from 'chai'; import {config} from 'src/config.js'; -import {getRefererInfo} from 'src/refererDetection.js'; -import {processFpd, registerSubmodules, startAuctionHook, reset} from 'modules/fpdModule/index.js'; -import * as enrichmentModule from 'modules/enrichmentFpdModule.js'; -import * as validationModule from 'modules/validationFpdModule/index.js'; -import {resetEnrichments} from 'modules/enrichmentFpdModule.js'; -import {GreedyPromise} from '../../../src/utils/promise.js'; +import {registerSubmodules, reset, startAuctionHook} from 'modules/fpdModule/index.js'; describe('the first party data module', function () { afterEach(function () { config.resetConfig(); }); + after(() => { + reset(); + }) describe('startAuctionHook', () => { const mockFpd = { @@ -50,282 +48,4 @@ describe('the first party data module', function () { }); }); }); - - describe('first party data intitializing', function () { - let width; - let widthStub; - let height; - let heightStub; - let querySelectorStub; - let canonical; - let keywords; - let lowEntropySuaStub; - let highEntropySuaStub; - - before(function() { - reset(); - registerSubmodules(enrichmentModule); - registerSubmodules(validationModule); - - canonical = document.createElement('link'); - canonical.rel = 'canonical'; - keywords = document.createElement('meta'); - keywords.name = 'keywords'; - }); - - beforeEach(function() { - resetEnrichments(); - querySelectorStub = sinon.stub(window.top.document, 'querySelector'); - querySelectorStub.withArgs("link[rel='canonical']").returns(canonical); - querySelectorStub.withArgs("meta[name='keywords']").returns(keywords); - widthStub = sinon.stub(window.top, 'innerWidth').get(function () { - return width; - }); - heightStub = sinon.stub(window.top, 'innerHeight').get(function () { - return height; - }); - lowEntropySuaStub = sinon.stub(enrichmentModule.sua, 'le').callsFake(() => null); - highEntropySuaStub = sinon.stub(enrichmentModule.sua, 'he').callsFake(() => GreedyPromise.resolve()); - }); - - afterEach(function() { - widthStub.restore(); - heightStub.restore(); - querySelectorStub.restore(); - canonical = document.createElement('link'); - canonical.rel = 'canonical'; - keywords = document.createElement('meta'); - keywords.name = 'keywords'; - lowEntropySuaStub.restore(); - highEntropySuaStub.restore(); - }); - - it('filters ortb2 data that is set', function () { - const global = { - user: { - data: {}, - gender: 'f', - age: 45 - }, - site: { - content: { - data: [{ - segment: { - test: 1 - }, - name: 'foo' - }, { - segment: [{ - id: 'test' - }, { - id: 3 - }], - name: 'bar' - }] - } - }, - device: { - w: 1, - h: 1 - } - }; - - config.setConfig({'pageUrl': 'https://www.domain.com/path?query=12345'}); - width = 1120; - height = 750; - - return processFpd({global}).then(({global: validated}) => { - expect(validated.site.ref).to.equal(getRefererInfo().ref || undefined); - expect(validated.site.page).to.equal('https://www.domain.com/path?query=12345'); - expect(validated.site.domain).to.equal('domain.com'); - expect(validated.site.content.data).to.deep.equal([{segment: [{id: 'test'}], name: 'bar'}]); - expect(validated.user.data).to.be.undefined; - expect(validated.device).to.deep.to.equal({w: 1, h: 1}); - expect(validated.site.keywords).to.be.undefined; - }); - }); - - it('should not overwrite existing data with default settings', function () { - const global = { - site: { - ref: 'https://referer.com' - } - }; - - return processFpd({global}).then(({global: validated}) => { - expect(validated.site.ref).to.equal('https://referer.com'); - }); - }); - - it('should allow overwrite default data with setConfig', function () { - const global = { - site: { - ref: 'https://referer.com' - } - }; - - return processFpd({global}).then(({global: validated}) => { - expect(validated.site.ref).to.equal('https://referer.com'); - }); - }); - - it('should filter all data', function () { - let global = { - imp: [], - site: { - name: 123, - domain: 456, - page: 789, - ref: 987, - keywords: ['keywords'], - search: 654, - cat: 'cat', - sectioncat: 'sectioncat', - pagecat: 'pagecat', - content: { - data: [{ - name: 1, - segment: [] - }] - } - }, - user: { - yob: 'twenty', - gender: 0, - keywords: ['foobar'], - data: ['test'] - }, - device: [800, 450], - cur: { - adServerCurrency: 'USD' - } - }; - config.setConfig({'firstPartyData': {skipEnrichments: true}}); - return processFpd({global}).then(({global: validated}) => { - expect(validated).to.deep.equal({}); - }); - }); - - it('should add enrichments but not alter any arbitrary ortb2 data', function () { - let global = { - site: { - ext: { - data: { - inventory: ['value1'] - } - } - }, - user: { - ext: { - data: { - visitor: ['value2'] - } - } - }, - cur: ['USD'] - }; - return processFpd({global}).then(({global: validated}) => { - expect(validated.site.ref).to.equal(getRefererInfo().referer); - expect(validated.site.ext.data).to.deep.equal({inventory: ['value1']}); - expect(validated.user.ext.data).to.deep.equal({visitor: ['value2']}); - expect(validated.cur).to.deep.equal(['USD']); - }) - }); - - it('should filter bidderConfig data', function () { - let bidder = { - bidderA: { - site: { - keywords: 'other', - ref: 'https://domain.com' - }, - user: { - keywords: 'test', - data: [{ - segment: [{id: 4}], - name: 't' - }] - } - } - }; - - return processFpd({bidder}).then(({bidder: validated}) => { - expect(validated.bidderA).to.not.be.undefined; - expect(validated.bidderA.user.data).to.be.undefined; - expect(validated.bidderA.user.keywords).to.equal('test'); - expect(validated.bidderA.site.keywords).to.equal('other'); - expect(validated.bidderA.site.ref).to.equal('https://domain.com'); - }) - }); - - it('should not filter bidderConfig data as it is valid', function () { - let bidder = { - bidderA: { - site: { - keywords: 'other', - ref: 'https://domain.com' - }, - user: { - keywords: 'test', - data: [{ - segment: [{id: 'data1_id'}], - name: 'data1' - }] - } - } - }; - - return processFpd({bidder}).then(({bidder: validated}) => { - expect(validated.bidderA).to.not.be.undefined; - expect(validated.bidderA.user.data).to.deep.equal([{segment: [{id: 'data1_id'}], name: 'data1'}]); - expect(validated.bidderA.user.keywords).to.equal('test'); - expect(validated.bidderA.site.keywords).to.equal('other'); - expect(validated.bidderA.site.ref).to.equal('https://domain.com'); - }); - }); - - it('should not set default values if skipEnrichments is turned on', function () { - config.setConfig({'firstPartyData': {skipEnrichments: true}}); - - let global = { - site: { - keywords: 'other' - }, - user: { - keywords: 'test', - data: [{ - segment: [{id: 'data1_id'}], - name: 'data1' - }] - } - }; - - return processFpd({global}).then(({global: validated}) => { - expect(validated.device).to.be.undefined; - expect(validated.site.ref).to.be.undefined; - expect(validated.site.page).to.be.undefined; - expect(validated.site.domain).to.be.undefined; - }); - }); - - it('should not validate ortb2 data if skipValidations is turned on', function () { - config.setConfig({'firstPartyData': {skipValidations: true}}); - - let global = { - site: { - keywords: 'other' - }, - user: { - keywords: 'test', - data: [{ - segment: [{id: 'nonfiltered'}] - }] - } - }; - - return processFpd({global}).then(({global: validated}) => { - expect(validated.user.data).to.deep.equal([{segment: [{id: 'nonfiltered'}]}]); - }); - }); - }); }); diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index b3b01dc93b5..400b9145e0b 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -15,6 +15,8 @@ import 'modules/consentManagementUsp.js'; import 'modules/schain.js'; import {decorateAdUnitsWithNativeParams, toLegacyResponse} from '../../../src/native.js'; import {createEidsArray} from '../../../modules/userId/eids.js'; +import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; +import {hook} from '../../../src/hook.js'; describe('Improve Digital Adapter Tests', function () { const METHOD = 'POST'; @@ -168,6 +170,10 @@ describe('Improve Digital Adapter Tests', function () { return bidRequests; } + before(() => { + hook.ready(); + }); + describe('isBidRequestValid', function () { it('should return false when no bid', function () { expect(spec.isBidRequestValid()).to.equal(false); @@ -220,7 +226,7 @@ describe('Improve Digital Adapter Tests', function () { it('should make a well-formed request objects', function () { getConfigStub = sinon.stub(config, 'getConfig'); getConfigStub.withArgs('improvedigital.usePrebidSizes').returns(true); - const request = spec.buildRequests([simpleBidRequest], bidderRequest)[0]; + const request = spec.buildRequests([simpleBidRequest], syncAddFPDToBidderRequest(bidderRequest))[0]; expect(request).to.be.an('object'); expect(request.method).to.equal(METHOD); expect(request.url).to.equal(AD_SERVER_URL); @@ -390,7 +396,7 @@ describe('Improve Digital Adapter Tests', function () { it('should add GDPR consent string', function () { const bidRequest = Object.assign({}, simpleBidRequest); - const payload = JSON.parse(spec.buildRequests([bidRequest], bidderRequestGdpr)[0].data); + const payload = JSON.parse(spec.buildRequests([bidRequest], syncAddFPDToBidderRequest(bidderRequestGdpr))[0].data); expect(payload.regs.ext.gdpr).to.exist.and.to.equal(1); expect(payload.user.ext.consent).to.equal('CONSENT'); expect(payload.user.ext.ConsentedProvidersSettings).to.not.exist; @@ -401,13 +407,13 @@ describe('Improve Digital Adapter Tests', function () { const bidderRequestGdprEmptyAddtl = deepClone(bidderRequestGdpr); bidderRequestGdprEmptyAddtl.gdprConsent.addtlConsent = '1~'; const bidRequest = Object.assign({}, simpleBidRequest); - const payload = JSON.parse(spec.buildRequests([bidRequest], bidderRequestGdprEmptyAddtl)[0].data); + const payload = JSON.parse(spec.buildRequests([bidRequest], syncAddFPDToBidderRequest(bidderRequestGdprEmptyAddtl))[0].data); expect(payload.user.ext.consented_providers_settings).to.not.exist; }); it('should add ConsentedProvidersSettings when extend mode enabled', function () { const bidRequest = deepClone(extendBidRequest); - const payload = JSON.parse(spec.buildRequests([bidRequest], bidderRequestGdpr)[0].data); + const payload = JSON.parse(spec.buildRequests([bidRequest], syncAddFPDToBidderRequest(bidderRequestGdpr))[0].data); expect(payload.regs.ext.gdpr).to.exist.and.to.equal(1); expect(payload.user.ext.consent).to.equal('CONSENT'); expect(payload.user.ext.ConsentedProvidersSettings.consented_providers).to.exist.and.to.equal('1~1.35.41.101'); @@ -416,7 +422,7 @@ describe('Improve Digital Adapter Tests', function () { it('should add CCPA consent string', function () { const bidRequest = Object.assign({}, simpleBidRequest); - const request = spec.buildRequests([bidRequest], {...bidderRequest, ...{ uspConsent: '1YYY' }}); + const request = spec.buildRequests([bidRequest], syncAddFPDToBidderRequest({...bidderRequest, ...{ uspConsent: '1YYY' }})); const payload = JSON.parse(request[0].data); expect(payload.regs.ext.us_privacy).to.equal('1YYY'); }); @@ -425,17 +431,17 @@ describe('Improve Digital Adapter Tests', function () { getConfigStub = sinon.stub(config, 'getConfig'); getConfigStub.withArgs('coppa').returns(true); let bidRequest = Object.assign({}, simpleBidRequest); - let payload = JSON.parse(spec.buildRequests([bidRequest], bidderRequestGdpr)[0].data); + let payload = JSON.parse(spec.buildRequests([bidRequest], syncAddFPDToBidderRequest(bidderRequestGdpr))[0].data); expect(payload.regs.coppa).to.equal(1); getConfigStub.withArgs('coppa').returns(false); bidRequest = Object.assign({}, simpleBidRequest); - payload = JSON.parse(spec.buildRequests([bidRequest], bidderRequestGdpr)[0].data); + payload = JSON.parse(spec.buildRequests([bidRequest], syncAddFPDToBidderRequest(bidderRequestGdpr))[0].data); expect(payload.regs.coppa).to.equal(0); }); it('should add referrer', function () { const bidRequest = Object.assign({}, simpleBidRequest); - const request = spec.buildRequests([bidRequest], bidderRequestReferrer)[0]; + const request = spec.buildRequests([bidRequest], syncAddFPDToBidderRequest(bidderRequestReferrer))[0]; const payload = JSON.parse(request.data); expect(payload.site.page).to.equal('https://blah.com/test.html'); }); @@ -678,21 +684,21 @@ describe('Improve Digital Adapter Tests', function () { page: 'https://improveditigal.com/', domain: 'improveditigal.com' }); - let request = spec.buildRequests([simpleBidRequest], bidderRequestReferrer)[0]; + let request = spec.buildRequests([simpleBidRequest], syncAddFPDToBidderRequest(bidderRequestReferrer))[0]; let payload = JSON.parse(request.data); expect(payload.site.content).does.exist.and.equal('XYZ'); expect(payload.site.page).does.exist.and.equal('https://improveditigal.com/'); expect(payload.site.domain).does.exist.and.equal('improveditigal.com'); getConfigStub.reset(); - request = spec.buildRequests([simpleBidRequest], bidderRequestReferrer)[0]; + request = spec.buildRequests([simpleBidRequest], syncAddFPDToBidderRequest(bidderRequestReferrer))[0]; payload = JSON.parse(request.data); expect(payload.site.content).does.not.exist; expect(payload.site.page).does.exist.and.equal('https://blah.com/test.html'); expect(payload.site.domain).does.exist.and.equal('blah.com'); const ortb2 = {site: {content: 'ZZZ'}}; - request = spec.buildRequests([simpleBidRequest], {...bidderRequestReferrer, ortb2})[0]; + request = spec.buildRequests([simpleBidRequest], syncAddFPDToBidderRequest({...bidderRequestReferrer, ortb2}))[0]; payload = JSON.parse(request.data); expect(payload.site.content).does.exist.and.equal('ZZZ'); expect(payload.site.page).does.exist.and.equal('https://blah.com/test.html'); diff --git a/test/spec/modules/openxOrtbBidAdapter_spec.js b/test/spec/modules/openxOrtbBidAdapter_spec.js index 09e76d6ca7a..be6427f607b 100644 --- a/test/spec/modules/openxOrtbBidAdapter_spec.js +++ b/test/spec/modules/openxOrtbBidAdapter_spec.js @@ -14,10 +14,16 @@ import 'modules/consentManagement.js'; import 'modules/consentManagementUsp.js'; import 'modules/schain.js'; import {deepClone} from 'src/utils.js'; +import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; +import {hook} from '../../../src/hook.js'; const DEFAULT_SYNC = SYNC_URL + '?ph=' + DEFAULT_PH; describe('OpenxRtbAdapter', function () { + before(() => { + hook.ready(); + }); + const adapter = newBidder(spec); describe('inherited functions', function () { @@ -634,14 +640,14 @@ describe('OpenxRtbAdapter', function () { }); it('should send a signal to specify that US Privacy applies to this request', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request[0].data.regs.ext.us_privacy).to.equal('1YYN'); expect(request[1].data.regs.ext.us_privacy).to.equal('1YYN'); }); it('should not send the regs object, when consent string is undefined', function () { delete bidderRequest.uspConsent; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request[0].data.regs?.us_privacy).to.not.exist; }); }); @@ -676,21 +682,21 @@ describe('OpenxRtbAdapter', function () { it('should send a signal to specify that GDPR applies to this request', function () { bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request[0].data.regs.ext.gdpr).to.equal(1); expect(request[1].data.regs.ext.gdpr).to.equal(1); }); it('should send the consent string', function () { bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request[0].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); expect(request[1].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); }); it('should send the addtlConsent string', function () { bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request[0].data.user.ext.ConsentedProvidersSettings.consented_providers).to.equal(bidderRequest.gdprConsent.addtlConsent); expect(request[1].data.user.ext.ConsentedProvidersSettings.consented_providers).to.equal(bidderRequest.gdprConsent.addtlConsent); }); @@ -698,7 +704,7 @@ describe('OpenxRtbAdapter', function () { it('should send a signal to specify that GDPR does not apply to this request', function () { bidderRequest.gdprConsent.gdprApplies = false; bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request[0].data.regs.ext.gdpr).to.equal(0); expect(request[1].data.regs.ext.gdpr).to.equal(0); }); @@ -707,7 +713,7 @@ describe('OpenxRtbAdapter', function () { 'but can send consent data, ', function () { delete bidderRequest.gdprConsent.gdprApplies; bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request[0].data.regs?.ext?.gdpr).to.not.be.ok; expect(request[0].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); expect(request[1].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); @@ -716,7 +722,7 @@ describe('OpenxRtbAdapter', function () { it('when consent string is undefined, should not send the consent string, ', function () { delete bidderRequest.gdprConsent.consentString; bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request[0].data.imp[0].ext.consent).to.equal(undefined); expect(request[1].data.imp[0].ext.consent).to.equal(undefined); }); @@ -725,7 +731,7 @@ describe('OpenxRtbAdapter', function () { context('coppa', function() { it('when there are no coppa param settings, should not send a coppa flag', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); + const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); expect(request[0].data.regs?.coppa).to.be.not.ok; }); @@ -738,7 +744,7 @@ describe('OpenxRtbAdapter', function () { return utils.deepAccess(mockConfig, key); }); - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); + const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); expect(request[0].data.regs.coppa).to.equal(1); }); @@ -760,21 +766,21 @@ describe('OpenxRtbAdapter', function () { it('when there is a do not track, should send a dnt', function () { doNotTrackStub.returns(1); - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); + const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); expect(request[0].data.device.dnt).to.equal(1); }); it('when there is not do not track, don\'t send dnt', function () { doNotTrackStub.returns(0); - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); + const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); expect(request[0].data.device.dnt).to.equal(0); }); it('when there is no defined do not track, don\'t send dnt', function () { doNotTrackStub.returns(null); - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); + const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); expect(request[0].data.device.dnt).to.equal(0); }); }); @@ -1503,5 +1509,4 @@ describe('OpenxRtbAdapter', function () { }); }); }); -}) -; +}); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index f5f77e61056..9da242381be 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import {expect} from 'chai'; import { PrebidServer as Adapter, resetSyncedStatus, @@ -7,31 +7,30 @@ import { } from 'modules/prebidServerBidAdapter/index.js'; import adapterManager from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; -import { ajax } from 'src/ajax.js'; -import { config } from 'src/config.js'; +import {deepAccess, deepClone} from 'src/utils.js'; +import {ajax} from 'src/ajax.js'; +import {config} from 'src/config.js'; import * as events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; -import { server } from 'test/mocks/xhr.js'; -import { createEidsArray } from 'modules/userId/eids.js'; -import { deepAccess, deepClone } from 'src/utils.js'; -import 'modules/appnexusBidAdapter.js' // appnexus alias test -import 'modules/rubiconBidAdapter.js' // rubicon alias test -import 'src/prebid.js' // $$PREBID_GLOBAL$$.aliasBidder test -import 'modules/currency.js' // adServerCurrency test -// also load modules that register ORTB processors -import 'modules/currency.js'; +import {server} from 'test/mocks/xhr.js'; +import {createEidsArray} from 'modules/userId/eids.js'; +import 'modules/appnexusBidAdapter.js'; // appnexus alias test +import 'modules/rubiconBidAdapter.js'; // rubicon alias test +import 'src/prebid.js'; // $$PREBID_GLOBAL$$.aliasBidder test +import 'modules/currency.js'; // adServerCurrency test import 'modules/userId/index.js'; import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagement.js'; import 'modules/consentManagementUsp.js'; import 'modules/schain.js'; -import { hook } from '../../../src/hook.js'; -import { decorateAdUnitsWithNativeParams } from '../../../src/native.js'; -import { auctionManager } from '../../../src/auctionManager.js'; -import { stubAuctionIndex } from '../../helpers/indexStub.js'; -import { registerBidder } from 'src/adapters/bidderFactory.js'; +import {hook} from '../../../src/hook.js'; +import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; +import {auctionManager} from '../../../src/auctionManager.js'; +import {stubAuctionIndex} from '../../helpers/indexStub.js'; +import {registerBidder} from 'src/adapters/bidderFactory.js'; import {getGlobal} from '../../../src/prebidGlobal.js'; +import {syncAddFPDEnrichments, syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; let CONFIG = { accountId: '1', @@ -544,6 +543,16 @@ const RESPONSE_OPENRTB_NATIVE = { ] }; +function addFpdEnrichmentsToS2SRequest(s2sReq, bidderRequests) { + return { + ...s2sReq, + ortb2Fragments: { + ...(s2sReq.ortb2Fragments || {}), + global: syncAddFPDToBidderRequest({...(bidderRequests?.[0] || {}), ortb2: s2sReq.ortb2Fragments?.global || {}}).ortb2 + } + } +} + describe('S2S Adapter', function () { let adapter, addBidResponse = sinon.spy(), @@ -766,7 +775,7 @@ describe('S2S Adapter', function () { let gdprBidRequest = utils.deepClone(BID_REQUESTS); gdprBidRequest[0].gdprConsent = mockConsent(); - adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, gdprBidRequest), gdprBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.regs.ext.gdpr).is.equal(1); @@ -775,7 +784,7 @@ describe('S2S Adapter', function () { config.resetConfig(); config.setConfig({ s2sConfig: CONFIG }); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); requestBid = JSON.parse(server.requests[1].requestBody); expect(requestBid.regs).to.not.exist; @@ -791,7 +800,7 @@ describe('S2S Adapter', function () { addtlConsent: 'superduperconsent', }); - adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, gdprBidRequest), gdprBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.regs.ext.gdpr).is.equal(1); @@ -877,13 +886,13 @@ describe('S2S Adapter', function () { $$PREBID_GLOBAL$$.requestBids.removeAll(); }); - it('is added to ortb2 request when in bidRequest', function () { + it('is added to ortb2 request when in FPD', function () { config.setConfig({ s2sConfig: CONFIG }); let uspBidRequest = utils.deepClone(BID_REQUESTS); uspBidRequest[0].uspConsent = '1NYN'; - adapter.callBids(REQUEST, uspBidRequest, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, uspBidRequest), uspBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.regs.ext.us_privacy).is.equal('1NYN'); @@ -891,7 +900,7 @@ describe('S2S Adapter', function () { config.resetConfig(); config.setConfig({ s2sConfig: CONFIG }); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); requestBid = JSON.parse(server.requests[1].requestBody); expect(requestBid.regs).to.not.exist; @@ -929,7 +938,7 @@ describe('S2S Adapter', function () { consentBidRequest[0].uspConsent = '1NYN'; consentBidRequest[0].gdprConsent = mockConsent(); - adapter.callBids(REQUEST, consentBidRequest, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, consentBidRequest), consentBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.regs.ext.us_privacy).is.equal('1NYN'); @@ -977,7 +986,7 @@ describe('S2S Adapter', function () { }; config.setConfig(_config); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); sinon.assert.match(requestBid.device, { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC', @@ -1004,7 +1013,7 @@ describe('S2S Adapter', function () { }; config.setConfig(_config); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); sinon.assert.match(requestBid.device, { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC', @@ -1367,7 +1376,7 @@ describe('S2S Adapter', function () { }; config.setConfig(_config); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); sinon.assert.match(requestBid.device, { w: window.innerWidth, @@ -1474,7 +1483,7 @@ describe('S2S Adapter', function () { }; config.setConfig(_config); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.site).to.exist.and.to.be.a('object'); expect(requestBid.site.publisher).to.exist.and.to.be.a('object'); @@ -1491,6 +1500,7 @@ describe('S2S Adapter', function () { content: { language: 'en' }, + domain: 'mytestpage.com', page: 'http://mytestpage.com' }); }); @@ -1815,7 +1825,7 @@ describe('S2S Adapter', function () { device: device }); - adapter.callBids(s2sBidRequest, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest(s2sBidRequest, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.site).to.exist.and.to.be.a('object'); @@ -2343,7 +2353,11 @@ describe('S2S Adapter', function () { })); const commonContextExpected = utils.mergeDeep({ 'page': 'http://mytestpage.com', - 'publisher': { 'id': '1' } + 'domain': 'mytestpage.com', + 'publisher': { + 'id': '1', + 'domain': 'mytestpage.com' + } }, commonSite); const ortb2Fragments = { @@ -2351,7 +2365,7 @@ describe('S2S Adapter', function () { bidder: Object.fromEntries(allowedBidders.map(bidder => [bidder, {site, user, bcat, badv}])) }; - adapter.callBids({...s2sBidRequest, ortb2Fragments}, bidRequests, addBidResponse, done, ajax); + adapter.callBids(addFpdEnrichmentsToS2SRequest({...s2sBidRequest, ortb2Fragments}, bidRequests), bidRequests, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.ext.prebid.bidderconfig).to.deep.equal(expected); expect(parsedRequestBody.site).to.deep.equal(commonContextExpected); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index f4abcc4b9f6..99e0681e547 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -2768,47 +2768,6 @@ describe('User ID', function () { }) }) }); - - describe('findRootDomain', function () { - let sandbox; - - beforeEach(function () { - init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule]); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [ - { - name: 'pubCommonId', - value: { pubcid: '11111' }, - }, - ], - }, - }); - sandbox = sinon.createSandbox(); - sandbox - .stub(coreStorage, 'getCookie') - .onFirstCall() - .returns(null) // .co.uk - .onSecondCall() - .returns('writeable'); // realdomain.co.uk; - }); - - afterEach(function () { - sandbox.restore(); - }); - - it('should just find the root domain', function () { - var domain = findRootDomain('sub.realdomain.co.uk'); - expect(domain).to.be.eq('realdomain.co.uk'); - }); - - it('should find the full domain when no subdomain is present', function () { - var domain = findRootDomain('realdomain.co.uk'); - expect(domain).to.be.eq('realdomain.co.uk'); - }); - }); }); describe('handles config with ESP configuration in user sync object', function() { diff --git a/test/spec/ortbConverter/default_processors_spec.js b/test/spec/ortbConverter/default_processors_spec.js deleted file mode 100644 index 28a35390b86..00000000000 --- a/test/spec/ortbConverter/default_processors_spec.js +++ /dev/null @@ -1,61 +0,0 @@ -import {setDevice, setSite} from '../../../libraries/ortbConverter/processors/default.js'; - -describe('setDevice', () => { - it('sets device.w, h, dnt, language, ua', () => { - const req = {}; - setDevice(req); - ['h', 'w', 'dnt', 'language', 'ua'].forEach(prop => expect(req.device[prop]).to.exist) - }); - - it('does not override FPD', () => { - const req = { - device: { - w: 'w', - h: 'h', - ext: {} - } - }; - setDevice(req); - sinon.assert.match(req.device, { - w: 'w', - h: 'h', - ext: {} - }) - }); -}); - -describe('setSite', () => { - const refererInfo = { - page: 'page.com', - ref: 'ref.com', - domain: 'domain.com' - }; - - it('sets site, domain, ref from refererInfo', () => { - const req = { - site: { - ext: {} - } - }; - setSite(req, {refererInfo}); - sinon.assert.match(req.site, refererInfo); - expect(req.site.ext).to.eql({}); - }); - - it('does not override FPD', () => { - const req = { - site: { - ref: 'other ref' - } - } - setSite(req, {refererInfo}); - expect(req.site.ref).to.eql('other ref'); - ['domain', 'page'].forEach(prop => expect(req.site[prop]).to.eql(refererInfo[prop])); - }); - - it('does not set null properties from refererInfo', () => { - const req = {}; - setSite(req, {refererInfo: {...refererInfo, ref: null}}); - expect(req.site.ref).to.not.exist; - }); -}); diff --git a/test/spec/ortbConverter/gdpr_spec.js b/test/spec/ortbConverter/gdpr_spec.js index 9851d7603b4..78fd1830438 100644 --- a/test/spec/ortbConverter/gdpr_spec.js +++ b/test/spec/ortbConverter/gdpr_spec.js @@ -1,33 +1,4 @@ -import {setOrtbAdditionalConsent, setOrtbGdpr} from '../../../modules/consentManagement.js'; - -describe('pbjs - ortb gpdr', () => { - it('sets gdpr properties from bidderRequest', () => { - const req = {}; - setOrtbGdpr(req, {gdprConsent: {gdprApplies: true, consentString: 'consent'}}); - expect(req.regs.ext.gdpr).to.eql(1); - expect(req.user.ext.consent).to.eql('consent'); - }); - - it('does not set it if missing', () => { - const req = {}; - setOrtbGdpr(req, {}); - expect(req).to.eql({}); - }); - - it('sets user.ext.consent, but not regs.ext.gdpr, if gpdrApplies is not a boolean', () => { - const req = {}; - setOrtbGdpr(req, { - gdprConsent: {consentString: 'mock-consent'} - }); - expect(req).to.eql({ - user: { - ext: { - consent: 'mock-consent' - } - } - }) - }); -}); +import {setOrtbAdditionalConsent} from '../../../modules/consentManagement.js'; describe('pbjs -> ortb addtlConsent', () => { it('sets ConsentedProvidersSettings', () => { diff --git a/test/spec/ortbConverter/usp_spec.js b/test/spec/ortbConverter/usp_spec.js deleted file mode 100644 index 01ff689cd97..00000000000 --- a/test/spec/ortbConverter/usp_spec.js +++ /dev/null @@ -1,15 +0,0 @@ -import {setOrtbUsp} from '../../../modules/consentManagementUsp.js'; - -describe('pbjs - ortb usp', () => { - it('sets regs.ext.us_privacy from bidderRequest', () => { - const req = {}; - setOrtbUsp(req, {uspConsent: '1NN'}); - expect(req.regs.ext.us_privacy).to.eql('1NN'); - }); - - it('does not set if missing', () => { - const req = {}; - setOrtbUsp(req, {}); - expect(req).to.eql({}); - }); -}) diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 8e3314bd954..9d8ea70e200 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -23,6 +23,8 @@ import $$PREBID_GLOBAL$$ from 'src/prebid.js'; import {resetAuctionState} from 'src/auction.js'; import {stubAuctionIndex} from '../../helpers/indexStub.js'; import {createBid} from '../../../src/bidfactory.js'; +import {enrichFPD} from '../../../src/fpd/enrichment.js'; +import {mockFpdEnrichments} from '../../helpers/fpd.js'; var assert = require('chai').assert; var expect = require('chai').expect; @@ -195,7 +197,7 @@ window.apntag = { } describe('Unit: Prebid Module', function () { - let bidExpiryStub + let bidExpiryStub, sandbox; before(() => { hook.ready(); @@ -205,12 +207,15 @@ describe('Unit: Prebid Module', function () { }); beforeEach(function () { + sandbox = sinon.sandbox.create(); + mockFpdEnrichments(sandbox); bidExpiryStub = sinon.stub(filters, 'isBidNotExpired').callsFake(() => true); configObj.setConfig({ useBidCache: true }); resetAuctionState(); }); afterEach(function() { + sandbox.restore(); $$PREBID_GLOBAL$$.adUnits = []; bidExpiryStub.restore(); configObj.setConfig({ useBidCache: false }); @@ -1749,39 +1754,62 @@ describe('Unit: Prebid Module', function () { configObj.resetConfig(); }); - it('passing global and auction-level FPD as ortb2Fragments.global', () => { - configObj.setConfig({ - ortb2: { + describe('with FPD', () => { + let globalFPD, auctionFPD, mergedFPD; + beforeEach(() => { + globalFPD = { 'k1': 'v1', 'k2': { 'k3': 'v3', 'k4': 'v4' } - } - }); - $$PREBID_GLOBAL$$.requestBids({ - ortb2: { + }; + auctionFPD = { 'k5': 'v5', 'k2': { 'k3': 'override', 'k7': 'v7' } - } - }); - sinon.assert.calledWith(startAuctionStub, sinon.match({ - ortb2Fragments: { - global: { - 'k1': 'v1', - 'k5': 'v5', - 'k2': { - 'k3': 'override', - 'k4': 'v4', - 'k7': 'v7' - } + }; + mergedFPD = { + 'k1': 'v1', + 'k5': 'v5', + 'k2': { + 'k3': 'override', + 'k4': 'v4', + 'k7': 'v7' } + }; + }); + + it('merged from setConfig and requestBids', () => { + configObj.setConfig({ortb2: globalFPD}); + $$PREBID_GLOBAL$$.requestBids({ortb2: auctionFPD}); + sinon.assert.calledWith(startAuctionStub, sinon.match({ + ortb2Fragments: {global: mergedFPD} + })); + }); + + it('enriched through enrichFPD', () => { + function enrich(next, fpd) { + next.bail(fpd.then(ortb2 => { + ortb2.enrich = true; + return ortb2; + })) } - })) + enrichFPD.before(enrich); + try { + configObj.setConfig({ortb2: globalFPD}); + $$PREBID_GLOBAL$$.requestBids({ortb2: auctionFPD}); + sinon.assert.calledWith(startAuctionStub, sinon.match({ + ortb2Fragments: {global: {...mergedFPD, enrich: true}} + })); + } finally { + enrichFPD.getHooks({hook: enrich}).remove(); + } + }) }); + it('passing bidder-specific FPD as ortb2Fragments.bidder', () => { configObj.setBidderConfig({ bidders: ['bidderA', 'bidderC'], From 747f1430f33f69ca683198ae7ca8c10473ff7ce9 Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Tue, 13 Dec 2022 19:59:40 +0000 Subject: [PATCH 521/569] Permutive RTD Module: add support for new ssp standard cohorts (#9236) * add logic to parse and pass ssp data to appnexus * simplify shouldSetConfig condition * Write SSP cohorts into p_standard targeting (+ bug fixes) * fix tests * Simplify * add logic to parse and pass ssp data to appnexus * simplify shouldSetConfig condition * Write SSP cohorts into p_standard targeting (+ bug fixes) * fix tests * Simplify * use new key for auction kw cohorts * Push SSP cohorts to SSPs via ORTB2 * Add tests and fix bugs * Update tests * update example with _pssps * Remove custom `appnexusAuctionKeywords` and use user.keywords in ortb2 config * Fix linting issues Co-authored-by: Paulius Imbrasas --- .../gpt/permutiveRtdProvider_example.html | 3 +- modules/permutiveRtdProvider.js | 61 ++++++++-- .../spec/modules/permutiveRtdProvider_spec.js | 113 ++++++++++++++++-- 3 files changed, 156 insertions(+), 21 deletions(-) diff --git a/integrationExamples/gpt/permutiveRtdProvider_example.html b/integrationExamples/gpt/permutiveRtdProvider_example.html index 118cc678726..554f2081c6d 100644 --- a/integrationExamples/gpt/permutiveRtdProvider_example.html +++ b/integrationExamples/gpt/permutiveRtdProvider_example.html @@ -15,7 +15,8 @@ _papns: ['appnexus1', 'appnexus2'], _psegs: ['1234', '1000001', '1000002'], _ppam: ['ppam1', 'ppam2'], - _pcrprs: ['pcrprs1', 'pcrprs2'] + _pcrprs: ['pcrprs1', 'pcrprs2'], + _pssps: { ssps: ['appnexus', 'some other'], cohorts: ['abcd', 'efgh', 'ijkl'] }, } for (let key in data) { diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index c11d1c12436..d62834cfcfc 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -14,6 +14,7 @@ import {includes} from '../src/polyfill.js'; const MODULE_NAME = 'permutive' export const PERMUTIVE_SUBMODULE_CONFIG_KEY = 'permutive-prebid-rtd' +export const PERMUTIVE_STANDARD_AUD_KEYWORD = 'p_standard_aud' export const storage = getStorageManager({gvlid: null, moduleName: MODULE_NAME}) @@ -118,9 +119,21 @@ export function setBidderRtb (bidderOrtb2, customModuleConfig) { const transformationConfigs = deepAccess(moduleConfig, 'params.transformations') || [] const segmentData = getSegments(maxSegs) - acBidders.forEach(function (bidder) { + const ssps = segmentData?.ssp?.ssps ?? [] + const sspCohorts = segmentData?.ssp?.cohorts ?? [] + + const bidders = new Set([...acBidders, ...ssps]) + bidders.forEach(function (bidder) { const currConfig = { ortb2: bidderOrtb2[bidder] || {} } - const nextConfig = updateOrtbConfig(currConfig, segmentData.ac, transformationConfigs) // ORTB2 uses the `ac` segment IDs + + const isAcBidder = acBidders.indexOf(bidder) > -1 + const isSspBidder = ssps.indexOf(bidder) > -1 + + let cohorts = [] + if (isAcBidder) cohorts = segmentData.ac + if (isSspBidder) cohorts = [...new Set([...cohorts, ...sspCohorts])].slice(0, maxSegs) + + const nextConfig = updateOrtbConfig(currConfig, cohorts, sspCohorts, transformationConfigs) bidderOrtb2[bidder] = nextConfig.ortb2; }) } @@ -131,9 +144,10 @@ export function setBidderRtb (bidderOrtb2, customModuleConfig) { * @param {Object[]} transformationConfigs - array of objects with `id` and `config` properties, used to determine * the transformations on user data to include the ORTB2 object * @param {string[]} segmentIDs - Permutive segment IDs + * @param {string[]} sspSegmentIDs - Permutive SSP segment IDs * @return {Object} Merged ortb2 object */ -function updateOrtbConfig (currConfig, segmentIDs, transformationConfigs) { +function updateOrtbConfig (currConfig, segmentIDs, sspSegmentIDs, transformationConfigs) { const name = 'permutive.com' const permutiveUserData = { @@ -154,6 +168,12 @@ function updateOrtbConfig (currConfig, segmentIDs, transformationConfigs) { deepSetValue(ortbConfig, 'ortb2.user.data', updatedUserData) + // As of writing this, only used for AppNexus/Xandr in place of appnexusAuctionKeywords in config + const currentUserKeywords = deepAccess(ortbConfig, 'ortb2.user.keywords') || '' + const keywords = sspSegmentIDs.map(segment => `${PERMUTIVE_STANDARD_AUD_KEYWORD}=${segment}`).join(',') + const updatedUserKeywords = (currentUserKeywords === '') ? keywords : `${currentUserKeywords},${keywords}` + deepSetValue(ortbConfig, 'ortb2.user.keywords', updatedUserKeywords) + return ortbConfig } @@ -222,10 +242,19 @@ function getCustomBidderFn (moduleConfig, bidder) { * @return {Object} Bidder function */ function getDefaultBidderFn (bidder) { + const isPStandardTargetingEnabled = (data, acEnabled) => { + return (acEnabled && data.ac && data.ac.length) || (data.ssp && data.ssp.cohorts.length) + } + const pStandardTargeting = (data, acEnabled) => { + const ac = (acEnabled) ? (data.ac ?? []) : [] + const ssp = data?.ssp?.cohorts ?? [] + return [...new Set([...ac, ...ssp])] + } const bidderMap = { appnexus: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.keywords.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.keywords.p_standard', segments) } if (data.appnexus && data.appnexus.length) { deepSetValue(bid, 'params.keywords.permutive', data.appnexus) @@ -234,19 +263,20 @@ function getDefaultBidderFn (bidder) { return bid }, rubicon: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.visitor.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.visitor.p_standard', segments) } if (data.rubicon && data.rubicon.length) { - const rubiconCohorts = deepAccess(bid, 'params.video') ? data.rubicon.map(String) : data.rubicon - deepSetValue(bid, 'params.visitor.permutive', rubiconCohorts) + deepSetValue(bid, 'params.visitor.permutive', data.rubicon.map(String)) } return bid }, ozone: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.customData.0.targeting.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.customData.0.targeting.p_standard', segments) } return bid @@ -290,10 +320,17 @@ export function getSegments (maxSegs) { rubicon: readSegments('_prubicons'), appnexus: readSegments('_papns'), gam: readSegments('_pdfps'), + ssp: readSegments('_pssps'), } for (const bidder in segments) { - segments[bidder] = segments[bidder].slice(0, maxSegs) + if (bidder === 'ssp') { + if (segments[bidder].cohorts && Array.isArray(segments[bidder].cohorts)) { + segments[bidder].cohorts = segments[bidder].cohorts.slice(0, maxSegs) + } + } else { + segments[bidder] = segments[bidder].slice(0, maxSegs) + } } return segments diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index 3f104ee1e2e..5030e662ea9 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -242,6 +242,42 @@ describe('permutiveRtdProvider', function () { }) }) it('should not overwrite ortb2 config', function () { + const moduleConfig = getConfig() + const acBidders = moduleConfig.params.acBidders + const sampleOrtbConfig = { + site: { + name: 'example' + }, + user: { + data: [ + { + name: 'www.dataprovider1.com', + ext: { taxonomyname: 'iab_audience_taxonomy' }, + segment: [{ id: '687' }, { id: '123' }] + } + ] + } + } + + const bidderConfig = Object.fromEntries(acBidders.map(bidder => [bidder, sampleOrtbConfig])) + + const transformedUserData = { + name: 'transformation', + ext: { test: true }, + segment: [1, 2, 3] + } + + setBidderRtb(bidderConfig, moduleConfig, { + // TODO: this argument is unused, is the test still valid / needed? + testTransformation: userData => transformedUserData + }) + + acBidders.forEach(bidder => { + expect(bidderConfig[bidder].site.name).to.equal(sampleOrtbConfig.site.name) + expect(bidderConfig[bidder].user.data).to.deep.include.members([sampleOrtbConfig.user.data[0]]) + }) + }) + it('should update user.keywords and not override existing values', function () { const moduleConfig = getConfig() const acBidders = moduleConfig.params.acBidders const sampleOrtbConfig = { @@ -275,9 +311,64 @@ describe('permutiveRtdProvider', function () { acBidders.forEach(bidder => { expect(bidderConfig[bidder].site.name).to.equal(sampleOrtbConfig.site.name) - expect(bidderConfig[bidder].user.keywords).to.equal(sampleOrtbConfig.user.keywords) expect(bidderConfig[bidder].user.data).to.deep.include.members([sampleOrtbConfig.user.data[0]]) + expect(bidderConfig[bidder].user.keywords).to.deep.equal('a,b,p_standard_aud=123,p_standard_aud=abc') + }) + }) + it('should merge ortb2 correctly for ac and ssps', function () { + setLocalStorage({ + '_ppam': [], + '_psegs': [], + '_pcrprs': ['abc', 'def', 'xyz'], + '_pssps': { + ssps: ['foo', 'bar'], + cohorts: ['xyz', 'uvw'], + } }) + const moduleConfig = { + name: 'permutive', + waitForIt: true, + params: { + acBidders: ['foo', 'other'], + maxSegs: 30 + } + } + const bidderConfig = {}; + + setBidderRtb(bidderConfig, moduleConfig) + + // include both ac and ssp cohorts, as foo is both in ac bidders and ssps + const expectedFooTargetingData = [ + { id: 'abc' }, + { id: 'def' }, + { id: 'xyz' }, + { id: 'uvw' }, + ] + expect(bidderConfig['foo'].user.data).to.deep.include.members([{ + name: 'permutive.com', + segment: expectedFooTargetingData + }]) + + // don't include ac targeting as it's not in ac bidders + const expectedBarTargetingData = [ + { id: 'xyz' }, + { id: 'uvw' }, + ] + expect(bidderConfig['bar'].user.data).to.deep.include.members([{ + name: 'permutive.com', + segment: expectedBarTargetingData + }]) + + // only include ac targeting as this ssp is not in ssps list + const expectedOtherTargetingData = [ + { id: 'abc' }, + { id: 'def' }, + { id: 'xyz' }, + ] + expect(bidderConfig['other'].user.data).to.deep.include.members([{ + name: 'permutive.com', + segment: expectedOtherTargetingData + }]) }) }) @@ -291,7 +382,11 @@ describe('permutiveRtdProvider', function () { const segments = getSegments(max) for (const key in segments) { - expect(segments[key]).to.have.length(max) + if (key === 'ssp') { + expect(segments[key].cohorts).to.have.length(max) + } else { + expect(segments[key]).to.have.length(max) + } } }) }) @@ -311,7 +406,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'appnexus') { expect(deepAccess(params, 'keywords.permutive')).to.eql(data.appnexus) - expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -332,7 +427,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'rubicon') { expect(deepAccess(params, 'visitor.permutive')).to.eql(data.rubicon) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -363,7 +458,7 @@ describe('permutiveRtdProvider', function () { deepAccess(params, 'visitor.permutive'), 'Should map all targeting values to a string', ).to.eql(data.rubicon.map(String)) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -383,7 +478,7 @@ describe('permutiveRtdProvider', function () { const { bidder, params } = bid if (bidder === 'ozone') { - expect(deepAccess(params, 'customData.0.targeting.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'customData.0.targeting.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -417,7 +512,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'rubicon') { expect(deepAccess(params, 'visitor.permutive')).to.eql(data.gam) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -555,6 +650,7 @@ function transformedTargeting (data = getTargetingData()) { appnexus: data._papns, rubicon: data._prubicons, gam: data._pdfps, + ssp: data._pssps, } } @@ -565,7 +661,8 @@ function getTargetingData () { _papns: ['appnexus1', 'appnexus2'], _psegs: ['1234', '1000001', '1000002'], _ppam: ['ppam1', 'ppam2'], - _pcrprs: ['pcrprs1', 'pcrprs2'] + _pcrprs: ['pcrprs1', 'pcrprs2', 'dup'], + _pssps: { ssps: ['xyz', 'abc', 'dup'], cohorts: ['123', 'abc'] } } } From dc7e77f7c11928ff0c966badfe2a17b8ea18b880 Mon Sep 17 00:00:00 2001 From: jxdeveloper1 <71084096+jxdeveloper1@users.noreply.github.com> Date: Wed, 14 Dec 2022 05:11:42 +0800 Subject: [PATCH 522/569] Jixie Bid Adapter: Add read jxtoko cookie (#9331) * Adapter does not seem capable of supporting advertiserDomains #6650 added response comment and some trivial code. * removed a blank line at the end of file added a space behind the // in comments * in response to comment from reviewer. add the aspect of advertiserdomain in unit tests * added the code to get the keywords from the meta tags if available. * added some cookie fetching --- modules/jixieBidAdapter.js | 2 ++ test/spec/modules/jixieBidAdapter_spec.js | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index f6f58883990..5eeab197f3a 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -56,6 +56,8 @@ function fetchIds_() { if (tmp) ret.client_id_ls = tmp; tmp = storage.getDataFromLocalStorage('_jxxs'); if (tmp) ret.session_id_ls = tmp; + tmp = storage.getCookie('_jxtoko'); + if (tmp) ret.jxtoko_id = tmp; } catch (error) {} return ret; } diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js index 0c999f1cd51..c354de3c3ac 100644 --- a/test/spec/modules/jixieBidAdapter_spec.js +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -71,6 +71,7 @@ describe('jixie Adapter', function () { const clientIdTest1_ = '1aba6a40-f711-11e9-868c-53a2ae972xxx'; const sessionIdTest1_ = '1594782644-1aba6a40-f711-11e9-868c-53a2ae972xxx'; + const jxtokoTest1_ = 'eyJJRCI6ImFiYyJ9'; // to serve as the object that prebid will call jixie buildRequest with: (param2) const bidderRequest_ = { @@ -198,6 +199,9 @@ describe('jixie Adapter', function () { // get the interceptors ready: let getCookieStub = sinon.stub(storage, 'getCookie'); let getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + getCookieStub + .withArgs('_jxtoko') + .returns(jxtokoTest1_); getCookieStub .withArgs('_jxx') .returns(clientIdTest1_); @@ -227,6 +231,7 @@ describe('jixie Adapter', function () { expect(payload).to.have.property('client_id_ls', clientIdTest1_); expect(payload).to.have.property('session_id_c', sessionIdTest1_); expect(payload).to.have.property('session_id_ls', sessionIdTest1_); + expect(payload).to.have.property('jxtoko_id', jxtokoTest1_); expect(payload).to.have.property('device', device_); expect(payload).to.have.property('domain', domain_); expect(payload).to.have.property('pageurl', pageurl_); From 1b1c2a828614a45fab50e1b2c3e3373b269e0ccd Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 14 Dec 2022 11:38:15 +0100 Subject: [PATCH 523/569] AdagioBidAdapter: add missing try-catch (#9338) --- modules/adagioBidAdapter.js | 75 ++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 8dcd95f933b..4f86a914982 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -761,46 +761,51 @@ function getSlotPosition(adUnitElementId) { position.x = Math.round(sfGeom.t); position.y = Math.round(sfGeom.l); } else if (canAccessTopWindow()) { - // window.top based computing - const wt = getWindowTop(); - const d = wt.document; + try { + // window.top based computing + const wt = getWindowTop(); + const d = wt.document; - let domElement; + let domElement; - if (inIframe() === true) { - const ws = getWindowSelf(); - const currentElement = ws.document.getElementById(adUnitElementId); - domElement = internal.getElementFromTopWindow(currentElement, ws); - } else { - domElement = wt.document.getElementById(adUnitElementId); - } + if (inIframe() === true) { + const ws = getWindowSelf(); + const currentElement = ws.document.getElementById(adUnitElementId); + domElement = internal.getElementFromTopWindow(currentElement, ws); + } else { + domElement = wt.document.getElementById(adUnitElementId); + } - if (!domElement) { - return ''; - } + if (!domElement) { + return ''; + } - let box = domElement.getBoundingClientRect(); - - const docEl = d.documentElement; - const body = d.body; - const clientTop = d.clientTop || body.clientTop || 0; - const clientLeft = d.clientLeft || body.clientLeft || 0; - const scrollTop = wt.pageYOffset || docEl.scrollTop || body.scrollTop; - const scrollLeft = wt.pageXOffset || docEl.scrollLeft || body.scrollLeft; - - const elComputedStyle = wt.getComputedStyle(domElement, null); - const elComputedDisplay = elComputedStyle.display || 'block'; - const mustDisplayElement = elComputedDisplay === 'none'; - - if (mustDisplayElement) { - domElement.style = domElement.style || {}; - const originalDisplay = domElement.style.display; - domElement.style.display = 'block'; - box = domElement.getBoundingClientRect(); - domElement.style.display = originalDisplay || null; + let box = domElement.getBoundingClientRect(); + + const docEl = d.documentElement; + const body = d.body; + const clientTop = d.clientTop || body.clientTop || 0; + const clientLeft = d.clientLeft || body.clientLeft || 0; + const scrollTop = wt.pageYOffset || docEl.scrollTop || body.scrollTop; + const scrollLeft = wt.pageXOffset || docEl.scrollLeft || body.scrollLeft; + + const elComputedStyle = wt.getComputedStyle(domElement, null); + const elComputedDisplay = elComputedStyle.display || 'block'; + const mustDisplayElement = elComputedDisplay === 'none'; + + if (mustDisplayElement) { + domElement.style = domElement.style || {}; + const originalDisplay = domElement.style.display; + domElement.style.display = 'block'; + box = domElement.getBoundingClientRect(); + domElement.style.display = originalDisplay || null; + } + position.x = Math.round(box.left + scrollLeft - clientLeft); + position.y = Math.round(box.top + scrollTop - clientTop); + } catch (err) { + logError(LOG_PREFIX, err); + return ''; } - position.x = Math.round(box.left + scrollLeft - clientLeft); - position.y = Math.round(box.top + scrollTop - clientTop); } else { return ''; } From 9abfb3d2ba3ed0160f3beaea394f438bd2fca99a Mon Sep 17 00:00:00 2001 From: Steffen Anders Date: Wed, 14 Dec 2022 13:24:11 +0100 Subject: [PATCH 524/569] AdUp Technology bid adapter: optimize floor price detection (#9332) --- modules/aduptechBidAdapter.js | 90 ++++++++----- test/spec/modules/aduptechBidAdapter_spec.js | 131 ++++++++++++++----- 2 files changed, 159 insertions(+), 62 deletions(-) diff --git a/modules/aduptechBidAdapter.js b/modules/aduptechBidAdapter.js index 5d8b69af77f..c39d9e14f17 100644 --- a/modules/aduptechBidAdapter.js +++ b/modules/aduptechBidAdapter.js @@ -1,4 +1,4 @@ -import {deepAccess, getAdUnitSizes} from '../src/utils.js'; +import {getAdUnitSizes, isArray, isBoolean, isEmpty, isFn, isPlainObject} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; @@ -23,7 +23,7 @@ export const internal = { if (bidderRequest && bidderRequest.gdprConsent) { return { consentString: bidderRequest.gdprConsent.consentString, - consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true + consentRequired: (isBoolean(bidderRequest.gdprConsent.gdprApplies)) ? bidderRequest.gdprConsent.gdprApplies : true }; } @@ -60,8 +60,26 @@ export const internal = { */ extractBannerConfig: (bidRequest) => { const sizes = getAdUnitSizes(bidRequest); - if (Array.isArray(sizes) && sizes.length > 0) { - return { sizes: sizes }; + if (isArray(sizes) && !isEmpty(sizes)) { + const banner = { sizes: sizes }; + + // try to add floor for each banner size + banner.sizes.forEach(size => { + const floor = internal.getFloor(bidRequest, { mediaType: BANNER, size }); + if (floor) { + size.push(floor.floor); + size.push(floor.currency); + } + }); + + // try to add default floor for banner + const floor = internal.getFloor(bidRequest, { mediaType: BANNER, size: '*' }); + if (floor) { + banner.floorPrice = floor.floor; + banner.floorCurrency = floor.currency; + } + + return banner; } return null; @@ -74,8 +92,17 @@ export const internal = { * @returns {null|Object.} */ extractNativeConfig: (bidRequest) => { - if (bidRequest && deepAccess(bidRequest, 'mediaTypes.native')) { - return bidRequest.mediaTypes.native; + if (bidRequest?.mediaTypes?.native) { + const native = bidRequest.mediaTypes.native; + + // try to add default floor for native + const floor = internal.getFloor(bidRequest, { mediaType: NATIVE, size: '*' }); + if (floor) { + native.floorPrice = floor.floor; + native.floorCurrency = floor.currency; + } + + return native; } return null; @@ -96,27 +123,25 @@ export const internal = { }, /** - * Extracts the floor price params from given bidRequest + * Try to get floor information via bidRequest.getFloor() * * @param {BidRequest} bidRequest - * @returns {undefined|float} + * @param {Object} options + * @returns {null|Object.} */ - extractFloorPrice: (bidRequest) => { - let floorPrice; - if (bidRequest && bidRequest.params && bidRequest.params.floor) { - // if there is a manual floorPrice set - floorPrice = !isNaN(parseInt(bidRequest.params.floor)) ? bidRequest.params.floor : undefined; - } - if (typeof bidRequest.getFloor === 'function') { - // use prebid floor module - let floorInfo; - try { - floorInfo = bidRequest.getFloor(); - } catch (e) {} - floorPrice = typeof floorInfo === 'object' && !isNaN(parseInt(floorInfo.floor)) ? floorInfo.floor : floorPrice; + getFloor: (bidRequest, options) => { + if (!isFn(bidRequest?.getFloor)) { + return null; } - return floorPrice; + try { + const floor = bidRequest.getFloor(options); + if (isPlainObject(floor) && !isNaN(floor.floor)) { + return floor; + } + } catch {} + + return null; }, /** @@ -128,11 +153,11 @@ export const internal = { groupBidRequestsByPublisher: (bidRequests) => { const groupedBidRequests = {}; - if (!bidRequests || bidRequests.length === 0) { + if (!bidRequests || isEmpty(bidRequests)) { return groupedBidRequests; } - bidRequests.forEach((bidRequest) => { + bidRequests.forEach(bidRequest => { const publisher = internal.extractParams(bidRequest).publisher; if (!publisher) { return; @@ -205,7 +230,7 @@ export const spec = { const requests = []; // stop here on invalid or empty data - if (!bidderRequest || !validBidRequests || validBidRequests.length === 0) { + if (!bidderRequest || !validBidRequests || isEmpty(validBidRequests)) { return requests; } @@ -237,7 +262,7 @@ export const spec = { } // handle multiple bids per request - groupedBidRequests[publisher].forEach((bidRequest) => { + groupedBidRequests[publisher].forEach(bidRequest => { const bid = { bidId: bidRequest.bidId, transactionId: bidRequest.transactionId, @@ -257,10 +282,11 @@ export const spec = { bid.native = nativeConfig; } - // add floor price - const floorPrice = internal.extractFloorPrice(bidRequest); - if (floorPrice) { - bid.floorPrice = floorPrice; + // try to add default floor + const floor = internal.getFloor(bidRequest, { mediaType: '*', size: '*' }); + if (floor) { + bid.floorPrice = floor.floor; + bid.floorCurrency = floor.currency; } request.data.imp.push(bid); @@ -282,12 +308,12 @@ export const spec = { const bidResponses = []; // stop here on invalid or empty data - if (!response || !deepAccess(response, 'body.bids') || response.body.bids.length === 0) { + if (!response?.body?.bids || isEmpty(response.body.bids)) { return bidResponses; } // parse multiple bids per response - response.body.bids.forEach((bid) => { + response.body.bids.forEach(bid => { if (!bid || !bid.bid || !bid.creative) { return; } diff --git a/test/spec/modules/aduptechBidAdapter_spec.js b/test/spec/modules/aduptechBidAdapter_spec.js index bbc4e554f7e..8dbdbbfeab5 100644 --- a/test/spec/modules/aduptechBidAdapter_spec.js +++ b/test/spec/modules/aduptechBidAdapter_spec.js @@ -182,6 +182,54 @@ describe('AduptechBidAdapter', () => { }); }); + describe('getFloor', () => { + let bidRequest; + + beforeEach(() => { + bidRequest = { + getFloor: sinon.stub() + }; + }); + + it('should handle empty or invalid bidRequest', () => { + expect(internal.getFloor(null)).to.be.null; + expect(internal.getFloor({})).to.be.null; + expect(internal.getFloor({ getFloor: 'foo' })).to.be.null; + }); + + it('should detect floor via getFloor()', () => { + const result = { + floor: 1.11, + currency: 'USD' + }; + + const options = { + mediaType: BANNER, + size: '*' + } + + bidRequest.getFloor.returns(result); + + expect(internal.getFloor(bidRequest, options)).to.deep.equal(result); + expect(bidRequest.getFloor.calledOnceWith(options)).to.be.true; + }); + + it('should handle empty, invalid or faulty getFloor() results', () => { + bidRequest.getFloor + .onCall(0).returns({}) + .onCall(1).returns({ floor: 'foo' }) + .onCall(2).returns('bar') + .onCall(3).throws(new Error('baz')); + + expect(internal.getFloor(bidRequest, {})).to.be.null; + expect(internal.getFloor(bidRequest, {})).to.be.null; + expect(internal.getFloor(bidRequest, {})).to.be.null; + expect(internal.getFloor(bidRequest, {})).to.be.null; + + expect(bidRequest.getFloor.callCount).to.equal(4); + }); + }); + describe('groupBidRequestsByPublisher', () => { it('should handle empty bidRequests', () => { expect(internal.groupBidRequestsByPublisher(null)).to.deep.equal({}); @@ -541,34 +589,35 @@ describe('AduptechBidAdapter', () => { } }; - const getFloorResponse = { - currency: 'USD', - floor: '1.23' - }; - - const validBidRequests = [ - { - bidId: 'bidId1', - adUnitCode: 'adUnitCode1', - transactionId: 'transactionId1', - mediaTypes: { - banner: { - sizes: [[100, 200], [300, 400]] - } - }, - params: { - publisher: 'publisher1', - placement: 'placement1' + const bidRequest = { + bidId: 'bidId1', + adUnitCode: 'adUnitCode1', + transactionId: 'transactionId1', + mediaTypes: { + banner: { + sizes: [[100, 200], [300, 400]] }, - getFloor: () => { - return getFloorResponse + native: { + image: { + required: true + }, } - } - ]; + }, + params: { + publisher: 'publisher1', + placement: 'placement1' + }, + getFloor: sinon.stub() + .onCall(0).returns({ floor: 1.11, currency: 'USD' }) + .onCall(1).returns({ floor: 2.22, currency: 'EUR' }) + .onCall(2).returns({ floor: 3.33, currency: 'USD' }) + .onCall(3).returns({ floor: 4.44, currency: 'GBP' }) + .onCall(4).returns({ floor: 5.55, currency: 'EUR' }) + }; - expect(spec.buildRequests(validBidRequests, bidderRequest)).to.deep.equal([ + expect(spec.buildRequests([bidRequest], bidderRequest)).to.deep.equal([ { - url: internal.buildEndpointUrl(validBidRequests[0].params.publisher), + url: internal.buildEndpointUrl(bidRequest.params.publisher), method: ENDPOINT_METHOD, data: { auctionId: bidderRequest.auctionId, @@ -580,17 +629,39 @@ describe('AduptechBidAdapter', () => { }, imp: [ { - bidId: validBidRequests[0].bidId, - transactionId: validBidRequests[0].transactionId, - adUnitCode: validBidRequests[0].adUnitCode, - params: validBidRequests[0].params, - banner: validBidRequests[0].mediaTypes.banner, - floorPrice: getFloorResponse.floor + bidId: bidRequest.bidId, + transactionId: bidRequest.transactionId, + adUnitCode: bidRequest.adUnitCode, + params: bidRequest.params, + banner: { + sizes: [ + [100, 200, 1.11, 'USD'], + [300, 400, 2.22, 'EUR'], + ], + floorPrice: 3.33, + floorCurrency: 'USD' + }, + native: { + image: { + required: true + }, + floorPrice: 4.44, + floorCurrency: 'GBP' + }, + floorPrice: 5.55, + floorCurrency: 'EUR' } ] } } ]); + + expect(bidRequest.getFloor.callCount).to.equal(5); + expect(bidRequest.getFloor.getCall(0).calledWith({ mediaType: BANNER, size: bidRequest.mediaTypes.banner.sizes[0] })).to.be.true; + expect(bidRequest.getFloor.getCall(1).calledWith({ mediaType: BANNER, size: bidRequest.mediaTypes.banner.sizes[1] })).to.be.true; + expect(bidRequest.getFloor.getCall(2).calledWith({ mediaType: BANNER, size: '*' })).to.be.true; + expect(bidRequest.getFloor.getCall(3).calledWith({ mediaType: NATIVE, size: '*' })).to.be.true; + expect(bidRequest.getFloor.getCall(4).calledWith({ mediaType: '*', size: '*' })).to.be.true; }); }); From 9f11a94397a6b90550dd2dd9456e66a70d773644 Mon Sep 17 00:00:00 2001 From: Mikhail Ivanchenko Date: Wed, 14 Dec 2022 16:13:00 +0300 Subject: [PATCH 525/569] nextMillenniumBidAdapter: improve getUserSyncs function (#9313) * add video support * improve userSync url * improve userSync url * Add tests for a new cases * use deepAccess instead of instanceof --- modules/nextMillenniumBidAdapter.js | 45 +++++++--- .../modules/nextMillenniumBidAdapter_spec.js | 83 +++++++++++-------- 2 files changed, 79 insertions(+), 49 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index cf732d343e5..b5d0e15d078 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -1,4 +1,5 @@ import { + isArray, _each, createTrackPixelHtml, deepAccess, @@ -24,7 +25,6 @@ const BIDDER_CODE = 'nextMillennium'; const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; const TEST_ENDPOINT = 'https://test.pbs.nextmillmedia.com/openrtb2/auction'; const REPORT_ENDPOINT = 'https://report2.hb.brainlyads.com/statistics/metric'; -const SYNC_ENDPOINT = 'https://cookies.nextmillmedia.com/sync?'; const TIME_TO_LIVE = 360; const VIDEO_PARAMS = [ 'api', 'linearity', 'maxduration', 'mimes', 'minduration', 'placement', @@ -208,20 +208,27 @@ export const spec = { getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { const pixels = []; - let syncUrl = SYNC_ENDPOINT; - if (gdprConsent && gdprConsent.gdprApplies) { - syncUrl += 'gdpr=1&gdpr_consent=' + gdprConsent.consentString; - } - if (uspConsent) { - syncUrl += 'us_privacy=' + uspConsent; - } + if (isArray(responses)) { + responses.forEach(response => { + if (syncOptions.pixelEnabled) { + deepAccess(response, 'body.ext.sync.image', []).forEach(imgUrl => { + pixels.push({ + type: 'image', + url: replaceUsersyncMacros(imgUrl, gdprConsent, uspConsent) + }); + }) + } - if (syncOptions.iframeEnabled) { - pixels.push({type: 'iframe', url: syncUrl + 'type=iframe'}); - } - if (syncOptions.pixelEnabled) { - pixels.push({type: 'image', url: syncUrl + 'type=image'}); + if (syncOptions.iframeEnabled) { + deepAccess(response, 'body.ext.sync.iframe', []).forEach(iframeUrl => { + pixels.push({ + type: 'iframe', + url: replaceUsersyncMacros(iframeUrl, gdprConsent, uspConsent) + }); + }) + } + }) } return pixels; @@ -263,6 +270,18 @@ export const spec = { }, }; +function replaceUsersyncMacros(url, gdprConsent, uspConsent) { + const { consentString, gdprApplies } = gdprConsent; + + return url.replace( + '{{.GDPR}}', Number(gdprApplies) + ).replace( + '{{.GDPRConsent}}', consentString + ).replace( + '{{.USPrivacy}}', uspConsent + ); +}; + function getAdEl(bid) { // best way I could think of to get El, is by matching adUnitCode to google slots... const slot = window.googletag && window.googletag.pubads && window.googletag.pubads().getSlots().find(slot => slot.getAdUnitPath() === bid.adUnitCode); diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 3094c1349f7..90f0d8ae15c 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -28,6 +28,34 @@ describe('nextMillenniumBidAdapterTests', function() { } ]; + const serverResponse = { + body: { + id: 'f7b3d2da-e762-410c-b069-424f92c4c4b2', + seatbid: [ + { + bid: [ + { + id: '7457329903666272789', + price: 0.5, + adm: 'Hello! It\'s a test ad!', + adid: '96846035', + adomain: ['test.addomain.com'], + w: 300, + h: 250 + } + ] + } + ], + cur: 'USD', + ext: { + sync: { + image: ['urlA'], + iframe: ['urlB'], + } + } + } + }; + const bidRequestDataGI = [ { adUnitCode: 'test-banner-gi', @@ -96,6 +124,24 @@ describe('nextMillenniumBidAdapterTests', function() { expect(JSON.parse(request[0].data).regs.ext.gdpr).to.equal(1); }); + it('Test getUserSyncs function', function () { + const syncOptions = { + 'iframeEnabled': false, + 'pixelEnabled': true + } + let userSync = spec.getUserSyncs(syncOptions, [serverResponse], bidRequestData[0].gdprConsent, bidRequestData[0].uspConsent); + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.equal('image'); + expect(userSync[0].url).to.equal('urlA'); + + syncOptions.iframeEnabled = true; + syncOptions.pixelEnabled = false; + userSync = spec.getUserSyncs(syncOptions, [serverResponse], bidRequestData[0].gdprConsent, bidRequestData[0].uspConsent); + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.equal('iframe'); + expect(userSync[0].url).to.equal('urlB'); + }); + it('Request params check without GDPR Consent', function () { delete bidRequestData[0].gdprConsent const request = spec.buildRequests(bidRequestData, bidRequestData[0]); @@ -137,51 +183,16 @@ describe('nextMillenniumBidAdapterTests', function() { it('Check if imp object was added', function() { const request = spec.buildRequests(bidRequestData) expect(JSON.parse(request[0].data).imp).to.be.an('array') - }) + }); it('Check if imp prebid stored id is correct', function() { const request = spec.buildRequests(bidRequestData) const requestData = JSON.parse(request[0].data); const storedReqId = requestData.ext.prebid.storedrequest.id; expect(requestData.imp[0].ext.prebid.storedrequest.id).to.equal(storedReqId) - }) - - it('Test getUserSyncs function', function () { - const syncOptions = { - 'iframeEnabled': false, - 'pixelEnabled': true - } - const userSync = spec.getUserSyncs(syncOptions); - expect(userSync).to.be.an('array').with.lengthOf(1); - expect(userSync[0].type).to.exist; - expect(userSync[0].url).to.exist; - expect(userSync[0].type).to.be.equal('image'); - expect(userSync[0].url).to.be.equal('https://cookies.nextmillmedia.com/sync?type=image'); }); it('validate_response_params', function() { - const serverResponse = { - body: { - id: 'f7b3d2da-e762-410c-b069-424f92c4c4b2', - seatbid: [ - { - bid: [ - { - id: '7457329903666272789', - price: 0.5, - adm: 'Hello! It\'s a test ad!', - adid: '96846035', - adomain: ['test.addomain.com'], - w: 300, - h: 250 - } - ] - } - ], - cur: 'USD' - } - }; - let bids = spec.interpretResponse(serverResponse, bidRequestData[0]); expect(bids).to.have.lengthOf(1); From 14bdd9d36957bf59e87b965cc871deae2ac24929 Mon Sep 17 00:00:00 2001 From: Lionell Pack Date: Thu, 15 Dec 2022 08:58:31 +1100 Subject: [PATCH 526/569] Uid2 module: major implementation change (#9264) * Complete the UID2 integration. Update docs. Add tests. * Removed some unnecessary code in uid2IdSystem.uid2IdSystem. Improved log messages. Pass through configured baseUrl. Tidied up some in-progress code problems. Added a timer mock to track and clear timers at the end of each test, to prevent interference. Improved testing code and fixed some bugs. * Move cookie cleanup into the after so it doesn't leave a mess behind for subsequent tests. Allow specifying multiple --file options when running/watching tests. * Provide an additional mock object for some test environments which don't provide crypto.subtle. * Improve some documentation for the UID2 module. * Improved UID2 module logging when debug flag is enabled. * Added tests around the api base url config for UID2. Added the new UID2 config to the example. * Update integration example to attempt a token refresh (it will fail due to not being a valid token). * Refactor to avoid duplicating cookie read code. Add a test for the case when the id value is provided directly in config without making use of the new token refresh system. * Fix an incorrect log call. Co-authored-by: Lionell Pack --- integrationExamples/gpt/userId_example.html | 12 +- karma.conf.maker.js | 2 +- modules/uid2IdSystem.js | 248 ++++++++++++++-- modules/uid2IdSystem.md | 34 ++- test/mocks/timers.js | 84 ++++++ test/spec/modules/uid2IdSystem_spec.js | 310 ++++++++++++++++++++ 6 files changed, 655 insertions(+), 35 deletions(-) create mode 100644 test/mocks/timers.js create mode 100644 test/spec/modules/uid2IdSystem_spec.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 5a6c80c4adf..4f94c73c281 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -249,7 +249,17 @@ } }, { - "name": "uid2" + "name": "uid2", + "params": { + "uid2Token": { + "advertising_token": "example token", + "refresh_token": "aslkdjaslkjdaslkhj", + "identity_expires": Date.now() + 60*1000, + "refresh_from": Date.now() - 10*1000, + "refresh_expires": Date.now() + 12*60*60*1000, + "refresh_response_key": null + } + } } , { diff --git a/karma.conf.maker.js b/karma.conf.maker.js index b20f73d74bc..692db7755e8 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -110,7 +110,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file, disableFe var webpackConfig = newWebpackConfig(codeCoverage, disableFeatures); var plugins = newPluginsArray(browserstack); - var files = file ? ['test/test_deps.js', file] : ['test/test_index.js']; + var files = file ? ['test/test_deps.js', file].flatMap(f => f) : ['test/test_index.js']; // This file opens the /debug.html tab automatically. // It has no real value unless you're running --watch, and intend to do some debugging in the browser. if (watchMode) { diff --git a/modules/uid2IdSystem.js b/modules/uid2IdSystem.js index 23656639532..9fd75f12591 100644 --- a/modules/uid2IdSystem.js +++ b/modules/uid2IdSystem.js @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ /** * This module adds uid2 ID support to the User ID module * The {@link module:modules/userId} module is required. @@ -10,48 +11,142 @@ import {submodule} from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; const MODULE_NAME = 'uid2'; +const MODULE_REVISION = `1.0`; +const PREBID_VERSION = '$prebid.version$'; +const UID2_CLIENT_ID = `PrebidJS-${PREBID_VERSION}-UID2Module-${MODULE_REVISION}`; const GVLID = 887; const LOG_PRE_FIX = 'UID2: '; const ADVERTISING_COOKIE = '__uid2_advertising_token'; -function readCookie() { - return storage.cookiesAreEnabled() ? storage.getCookie(ADVERTISING_COOKIE) : null; +// eslint-disable-next-line no-unused-vars +const UID2_TEST_URL = 'https://operator-integ.uidapi.com'; +const UID2_PROD_URL = 'https://prod.uidapi.com'; +const UID2_BASE_URL = UID2_PROD_URL; + +function getStorage() { + return getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME}); +} + +function createLogInfo(prefix) { + return function (...strings) { + logInfo(prefix + ' ', ...strings); + } } +export const storage = getStorage(); +const _logInfo = createLogInfo(LOG_PRE_FIX); function readFromLocalStorage() { return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(ADVERTISING_COOKIE) : null; } -function getStorage() { - return getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME}); +function readModuleCookie() { + const cookie = readCookie(ADVERTISING_COOKIE); + if (cookie && cookie.includes('{')) { + return JSON.parse(cookie); + } + return cookie; } -const storage = getStorage(); +function readJsonCookie(cookieName) { + return JSON.parse(readCookie(cookieName)); +} -const _logInfo = createLogInfo(LOG_PRE_FIX); +function readCookie(cookieName) { + const cookie = storage.cookiesAreEnabled() ? storage.getCookie(cookieName) : null; + if (!cookie) { + _logInfo(`Attempted to read UID2 from cookie '${cookieName}' but it was empty`); + return null; + }; + _logInfo(`Read UID2 from cookie '${cookieName}'`); + return cookie; +} -function createLogInfo(prefix) { - return function (...strings) { - logInfo(prefix + ' ', ...strings); +function storeValue(value) { + if (storage.cookiesAreEnabled()) { + storage.setCookie(ADVERTISING_COOKIE, JSON.stringify(value), Date.now() + 60 * 60 * 24 * 1000); + } else if (storage.localStorageIsEnabled()) { + storage.setLocalStorage(ADVERTISING_COOKIE, value); } } -/** - * Encode the id - * @param value - * @returns {string|*} - */ -function encodeId(value) { - const result = {}; - if (value) { - const bidIds = { - id: value +function isValidIdentity(identity) { + return !!(typeof identity === 'object' && identity !== null && identity.advertising_token && identity.identity_expires && identity.refresh_from && identity.refresh_token && identity.refresh_expires); +} + +// This is extracted from an in-progress API client. Once it's available via NPM, this class should be replaced with the NPM package. +class Uid2ApiClient { + constructor(opts) { + this._baseUrl = opts.baseUrl ? opts.baseUrl : UID2_BASE_URL; + this._clientVersion = UID2_CLIENT_ID; + } + createArrayBuffer(text) { + const arrayBuffer = new Uint8Array(text.length); + for (let i = 0; i < text.length; i++) { + arrayBuffer[i] = text.charCodeAt(i); } - result.uid2 = bidIds; - _logInfo('Decoded value ' + JSON.stringify(result)); - return result; + return arrayBuffer; + } + hasStatusResponse(response) { + return typeof (response) === 'object' && response && response.status; + } + isValidRefreshResponse(response) { + return this.hasStatusResponse(response) && ( + response.status === 'optout' || response.status === 'expired_token' || (response.status === 'success' && response.body && isValidIdentity(response.body)) + ); + } + ResponseToRefreshResult(response) { + if (this.isValidRefreshResponse(response)) { + if (response.status === 'success') { return { status: response.status, identity: response.body }; } + return response; + } else { return "Response didn't contain a valid status"; } + } + callRefreshApi(refreshDetails) { + const url = this._baseUrl + '/v2/token/refresh'; + const req = new XMLHttpRequest(); + req.overrideMimeType('text/plain'); + req.open('POST', url, true); + req.setRequestHeader('X-UID2-Client-Version', this._clientVersion); + let resolvePromise; + let rejectPromise; + const promise = new Promise((resolve, reject) => { + resolvePromise = resolve; + rejectPromise = reject; + }); + req.onreadystatechange = () => { + if (req.readyState !== req.DONE) { return; } + try { + if (!refreshDetails.refresh_response_key || req.status !== 200) { + _logInfo('Error status OR no response decryption key available, assuming unencrypted JSON'); + const response = JSON.parse(req.responseText); + const result = this.ResponseToRefreshResult(response); + if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); } + } else { + _logInfo('Decrypting refresh API response'); + const encodeResp = this.createArrayBuffer(atob(req.responseText)); + window.crypto.subtle.importKey('raw', this.createArrayBuffer(atob(refreshDetails.refresh_response_key)), { name: 'AES-GCM' }, false, ['decrypt']).then((key) => { + _logInfo('Imported decryption key') + // returns the symmetric key + window.crypto.subtle.decrypt({ + name: 'AES-GCM', + iv: encodeResp.slice(0, 12), + tagLength: 128, // The tagLength you used to encrypt (if any) + }, key, encodeResp.slice(12)).then((decrypted) => { + const decryptedResponse = String.fromCharCode(...new Uint8Array(decrypted)); + _logInfo('Decrypted to:', decryptedResponse); + const response = JSON.parse(decryptedResponse); + const result = this.ResponseToRefreshResult(response); + if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); } + }, (reason) => console.warn(`Call to UID2 API failed`, reason)); + }, (reason) => console.warn(`Call to UID2 API failed`, reason)); + } + } catch (err) { + rejectPromise(err); + } + }; + _logInfo('Sending refresh request', req); + req.send(refreshDetails.refresh_token); + return promise; } - return undefined; } /** @type {Submodule} */ @@ -71,27 +166,118 @@ export const uid2IdSubmodule = { * decode the stored id value for passing to bid requests * @function * @param {string} value - * @returns {{uid2:{ id: string }} or undefined if value doesn't exists + * @returns {{uid2:{ id: string } }} or undefined if value doesn't exists */ decode(value) { - return (value) ? encodeId(value) : undefined; + const result = decodeImpl(value); + _logInfo('UID2 decode returned', result); + return result; }, /** * performs action to obtain id and return a value. * @function - * @param {SubmoduleConfig} [config] + * @param {SubmoduleConfig} [configparams] * @param {ConsentData|undefined} consentData * @returns {uid2Id} */ getId(config, consentData) { - _logInfo('Creating UID 2.0'); - let value = readCookie() || readFromLocalStorage(); - _logInfo('The advertising token: ' + value); - return {id: value} + const result = getIdImpl(config, consentData); + _logInfo(`UID2 getId returned`, result); + return result; }, - }; +function refreshTokenAndStore(baseUrl, token) { + _logInfo('UID2 base url provided: ', baseUrl); + const client = new Uid2ApiClient({baseUrl}); + return client.callRefreshApi(token).then((response) => { + _logInfo('Refresh endpoint responded with:', response); + const tokens = { + originalToken: token, + latestToken: response.identity, + }; + storeValue(tokens); + return tokens; + }); +} + +function decodeImpl(value) { + if (typeof value === 'string') { + _logInfo('Found an old-style ID from an earlier version of the module. Refresh is unavailable for this token.'); + const result = { uid2: { id: value } }; + return result; + } + if (Date.now() < value.latestToken.identity_expires) { + return { uid2: { id: value.latestToken.advertising_token } }; + } + return null; +} + +function getIdImpl(config, consentData) { + let suppliedToken = null; + const uid2BaseUrl = config?.params?.uid2ApiBase ?? UID2_BASE_URL; + if (config && config.params) { + if (config.params.uid2Token) { + suppliedToken = config.params.uid2Token; + _logInfo('Read token from params', suppliedToken); + } else if (config.params.uid2ServerCookie) { + suppliedToken = readJsonCookie(config.params.uid2ServerCookie); + _logInfo('Read token from server-supplied cookie', suppliedToken); + } + } + let storedTokens = readModuleCookie() || readFromLocalStorage(); + _logInfo('Loaded module-stored tokens:', storedTokens); + + if (storedTokens && typeof storedTokens === 'string') { + // Legacy value stored, this must be from an old integration. If no token supplied, just use the legacy value. + + if (!suppliedToken) { + _logInfo('Returning legacy cookie value.'); + return { id: storedTokens }; + } + // Otherwise, ignore the legacy value - it should get over-written later anyway. + _logInfo('Discarding superseded legacy cookie.'); + storedTokens = null; + } + + if (suppliedToken && storedTokens) { + if (storedTokens.originalToken?.advertising_token !== suppliedToken.advertising_token) { + _logInfo('Server supplied new token - ignoring stored value.', storedTokens.originalToken?.advertising_token, suppliedToken.advertising_token); + // Stored token wasn't originally sourced from the provided token - ignore the stored value. A new user has logged in? + storedTokens = null; + } + } + // At this point, any legacy values or superseded stored tokens have been nulled out. + const useSuppliedToken = !(storedTokens?.latestToken) || (suppliedToken && suppliedToken.identity_expires > storedTokens.latestToken.identity_expires); + const newestAvailableToken = useSuppliedToken ? suppliedToken : storedTokens.latestToken; + _logInfo('UID2 module selected latest token', useSuppliedToken, newestAvailableToken); + if (!newestAvailableToken || Date.now() > newestAvailableToken.refresh_expires) { + _logInfo('Newest available token is expired and not refreshable.'); + return { id: null }; + } + if (Date.now() > newestAvailableToken.identity_expires) { + const promise = refreshTokenAndStore(uid2BaseUrl, newestAvailableToken); + _logInfo('Token is expired but can be refreshed, attempting refresh.'); + return { callback: (cb) => { + promise.then((result) => { + _logInfo('Refresh reponded, passing the updated token on.', result); + cb(result); + }); + } }; + } + // If should refresh (but don't need to), refresh in the background. + if (Date.now() > newestAvailableToken.refresh_from) { + _logInfo(`Refreshing token in background with low priority.`); + refreshTokenAndStore(uid2BaseUrl, newestAvailableToken); + } + const tokens = { + originalToken: suppliedToken ?? storedTokens?.originalToken, + latestToken: newestAvailableToken, + }; + storeValue(tokens); + return { id: tokens }; +} + // Register submodule for userId submodule('userId', uid2IdSubmodule); diff --git a/modules/uid2IdSystem.md b/modules/uid2IdSystem.md index fa596b17584..82ff1b3cb68 100644 --- a/modules/uid2IdSystem.md +++ b/modules/uid2IdSystem.md @@ -6,11 +6,23 @@ UID 2.0 ID Module. Individual params may be set for the UID 2.0 Submodule. At least one identifier must be set in the params. +The module will handle refreshing the token periodically and storing the updated token using the Prebid.js storage manager. If you provide an expired identity and the module has a valid identity which was refreshed from the identity you provide, it will use the refreshed identity. The module stores the original token used for refreshing the token, and it will use the refreshed tokens as long as the original token matches the one supplied. + ``` pbjs.setConfig({ userSync: { userIds: [{ - name: 'uid2' + name: 'uid2', + params: { + // Either: + uid2ServerCookie: 'your_UID2_server_set_cookie_name' + // Or: + uid2Token: { + 'advertising_token': '...', + 'refresh_token': '...', + // etc. - see the Sample Token below for contents of this object + } + } }] } }); @@ -18,7 +30,25 @@ pbjs.setConfig({ ## Parameter Descriptions for the `usersync` Configuration Section The below parameters apply only to the UID 2.0 User ID Module integration. +You should supply either `uid2Token` or `uid2ServerCookie`. + +If you provide `uid2Token`, the value should be a JavaScript/JSON object with the decrypted `body` payload response from a call to either `/token/generate` or `/token/refresh`. + +If you provide `uid2ServerCookie`, the module will expect that same JSON object to be stored in the cookie - i.e. it will pass the cookie value to `JSON.parse` and expect to receive an object containing similar to what you see in the `Sample token` section below. + +The module will make calls to the `/token/refresh` endpoint to update the token it stores internally, so bids may contain an updated token. + +If neither of `uid2Token` or `uid2ServerCookie` are supplied, and the module has stored a token using the Prebid.js storage system (typically in a cookie named `__uid2_advertising_token`), it will use that token. This cookie is internal to the module and should not be set directly. + +If a new token is supplied which does not match the original token used to generate any refreshed tokens, all stored tokens will be discarded and the new token used instead (refreshed if necessary). + +### Sample token + +`{`
  `"advertising_token": "...",`
  `"refresh_token": "...",`
  `"identity_expires": 1633643601000,`
  `"refresh_from": 1633643001000,`
  `"refresh_expires": 1636322000000,`
  `"refresh_response_key": "wR5t6HKMfJ2r4J7fEGX9Gw=="`
`}` + | Param under userSync.userIds[] | Scope | Type | Description | Example | | --- | --- | --- | --- | --- | | name | Required | String | ID value for the UID20 module - `"uid2"` | `"uid2"` | -| value | Optional | Object | Used only if the page has a separate mechanism for storing the UID 2.0 ID. The value is an object containing the values to be sent to the adapters. In this scenario, no URL is called and nothing is added to local storage | `{"uid2": { "id": "eb33b0cb-8d35-4722-b9c0-1a31d4064888"}}` | +| params.uid2Token | Optional | Object | The initial UID2 token. This should be `body` element of the decrypted response from a call to the `/token/generate` or `/token/refresh` endpoint. | See the sample token above. | +| params.uid2ServerCookie | Optional | String | The name of a cookie which holds the initial UID2 token, set by the server. The cookie should contain JSON in the same format as the alternative uid2Token param. **If uid2Token is supplied, this param is ignored.** | See the sample token above. | +| params.uid2ApiBase | Optional | String | Overrides the default UID2 API endpoint. | `https://prod.uidapi.com` _(default)_ | diff --git a/test/mocks/timers.js b/test/mocks/timers.js new file mode 100644 index 00000000000..6efd798c881 --- /dev/null +++ b/test/mocks/timers.js @@ -0,0 +1,84 @@ +/* + * Provides wrappers for timers to allow easy cancelling and/or awaiting of outstanding timers. + * This helps avoid functionality leaking from one test to the next. + */ + +let wrappersActive = false; + +export function configureTimerInterceptors(debugLog = function() {}, generateStackTraces = false) { + if (wrappersActive) throw new Error(`Timer wrappers are already in place.`); + wrappersActive = true; + let theseWrappersActive = true; + + let originalSetTimeout = setTimeout, originalSetInterval = setInterval, originalClearTimeout = clearTimeout, originalClearInterval = clearInterval; + + let timerId = -1; + let timers = []; + + const waitOnTimersResolves = []; + function checkWaits() { + if (timers.length === 0) waitOnTimersResolves.forEach((r) => r()); + } + const waitAllActiveTimers = () => timers.length === 0 ? Promise.resolve() : new Promise((resolve) => waitOnTimersResolves.push(resolve)); + const clearAllActiveTimers = () => timers.forEach((timer) => timer.type === 'timeout' ? clearTimeout(timer.handle) : clearInterval(timer.handle)); + + const generateInterceptor = (type, originalFunctionWrapper) => (fn, delay, ...args) => { + timerId++; + debugLog(`Setting wrapped timeout ${timerId} for ${delay ?? 0}`); + const info = { timerId, type }; + if (generateStackTraces) { + try { + throw new Error(); + } catch (ex) { + info.stack = ex.stack; + } + } + info.handle = originalFunctionWrapper(info, fn, delay, ...args); + timers.push(info); + return info.handle; + }; + const setTimeoutInterceptor = generateInterceptor('timeout', (info, fn, delay, ...args) => originalSetTimeout(() => { + try { + debugLog(`Running timeout ${info.timerId}`); + fn(...args); + } finally { + const infoIndex = timers.indexOf(info); + if (infoIndex > -1) timers.splice(infoIndex, 1); + checkWaits(); + } + }, delay)); + + const setIntervalInterceptor = generateInterceptor('interval', (info, fn, interval, ...args) => originalSetInterval(() => { + debugLog(`Running interval ${info.timerId}`); + fn(...args); + }, interval)); + + const generateClearInterceptor = (type, originalClearFunction) => (handle) => { + originalClearFunction(handle); + const infoIndex = timers.findIndex((i) => i.handle === handle && i.type === type); + if (infoIndex > -1) timers.splice(infoIndex, 1); + checkWaits(); + } + const clearTimeoutInterceptor = generateClearInterceptor('timeout', originalClearTimeout); + const clearIntervalInterceptor = generateClearInterceptor('interval', originalClearInterval); + + setTimeout = setTimeoutInterceptor; + setInterval = setIntervalInterceptor; + clearTimeout = clearTimeoutInterceptor; + clearInterval = clearIntervalInterceptor; + + return { + waitAllActiveTimers, + clearAllActiveTimers, + timers, + restore: () => { + if (theseWrappersActive) { + theseWrappersActive = false; + setTimeout = originalSetTimeout; + setInterval = originalSetInterval; + clearTimeout = originalClearTimeout; + clearInterval = originalClearInterval; + } + } + } +} diff --git a/test/spec/modules/uid2IdSystem_spec.js b/test/spec/modules/uid2IdSystem_spec.js new file mode 100644 index 00000000000..b33e8cda501 --- /dev/null +++ b/test/spec/modules/uid2IdSystem_spec.js @@ -0,0 +1,310 @@ +import {coreStorage, init, setSubmoduleRegistry, requestBidsHook} from 'modules/userId/index.js'; +import {config} from 'src/config.js'; +import * as utils from 'src/utils.js'; +import { uid2IdSubmodule } from 'modules/uid2IdSystem.js'; +import 'src/prebid.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { server } from 'test/mocks/xhr.js'; +import { configureTimerInterceptors } from 'test/mocks/timers.js'; +import {hook} from 'src/hook.js'; +import {uninstall as uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; + +let expect = require('chai').expect; + +const clearTimersAfterEachTest = true; +const debugOutput = () => {}; + +const expireCookieDate = 'Thu, 01 Jan 1970 00:00:01 GMT'; +const msIn12Hours = 60 * 60 * 12 * 1000; +const moduleCookieName = '__uid2_advertising_token'; +const publisherCookieName = '__UID2_SERVER_COOKIE'; +const auctionDelayMs = 10; +const legacyConfigParams = null; +const serverCookieConfigParams = { uid2ServerCookie: publisherCookieName } +const getFutureCookieExpiry = () => new Date(Date.now() + msIn12Hours).toUTCString(); +const setPublisherCookie = (token) => coreStorage.setCookie(publisherCookieName, JSON.stringify(token), getFutureCookieExpiry()); + +const makePrebidIdentityContainer = (token) => ({uid2: {id: token}}); +const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ + userSync: { auctionDelay: auctionDelayMs, userIds: [{name: 'uid2', params}] }, debug, ...extraSettings +}); + +const initialToken = `initial-advertising-token`; +const legacyToken = 'legacy-advertising-token'; +const refreshedToken = 'refreshed-advertising-token'; +const makeUid2Token = (token = initialToken, shouldRefresh = false, expired = false) => ({ + advertising_token: token, + refresh_token: 'fake-refresh-token', + identity_expires: expired ? Date.now() - 1000 : Date.now() + 60 * 60 * 1000, + refresh_from: shouldRefresh ? Date.now() - 1000 : Date.now() + 60 * 1000, + refresh_expires: Date.now() + 24 * 60 * 60 * 1000, // 24 hours + refresh_response_key: 'wR5t6HKMfJ2r4J7fEGX9Gw==', +}); +const expectInitialToken = (bid) => expect(bid?.userId ?? {}).to.deep.include(makePrebidIdentityContainer(initialToken)); +const expectRefreshedToken = (bid) => expect(bid?.userId ?? {}).to.deep.include(makePrebidIdentityContainer(refreshedToken)); +const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId'); +const expectGlobalToHaveRefreshedIdentity = () => expect(getGlobal().getUserIds()).to.deep.include(makePrebidIdentityContainer(refreshedToken)); +const expectGlobalToHaveNoUid2 = () => expect(getGlobal().getUserIds()).to.not.haveOwnProperty('uid2'); +const expectLegacyToken = (bid) => expect(bid.userId).to.deep.include(makePrebidIdentityContainer(legacyToken)); +const expectNoLegacyToken = (bid) => expect(bid.userId).to.not.deep.include(makePrebidIdentityContainer(legacyToken)); +const expectModuleCookieEmptyOrMissing = () => expect(coreStorage.getCookie(moduleCookieName)).to.be.null; +const expectModuleCookieToContain = (initialIdentity, latestIdentity) => { + const cookie = JSON.parse(coreStorage.getCookie(moduleCookieName)); + if (initialIdentity) expect(cookie.originalToken.advertising_token).to.equal(initialIdentity); + if (latestIdentity) expect(cookie.latestToken.advertising_token).to.equal(latestIdentity); +} + +const apiUrl = 'https://prod.uidapi.com/v2/token/refresh'; +const headers = { 'Content-Type': 'application/json' }; +const makeSuccessResponseBody = () => btoa(JSON.stringify({ status: 'success', body: { ...makeUid2Token(), advertising_token: refreshedToken } })); +const configureUid2Response = (httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); +const configureUid2ApiSuccessResponse = () => configureUid2Response(200, makeSuccessResponseBody()); +const configureUid2ApiFailResponse = () => configureUid2Response(500, 'Error'); + +const respondAfterDelay = (delay) => new Promise((resolve) => setTimeout(() => { + server.respond(); + setTimeout(() => resolve()); +}, delay)); + +const runAuction = async () => { + const adUnits = [{ + code: 'adUnit-code', + mediaTypes: {banner: {}, native: {}}, + sizes: [[300, 200], [300, 600]], + bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] + }]; + return new Promise(function(resolve) { + requestBidsHook(function() { + resolve(adUnits[0].bids[0]); + }, {adUnits}); + }); +} + +// Runs the provided test twice - once with a successful API mock, once with one which returns a server error +const testApiSuccessAndFailure = (act, testDescription, failTestDescription, only = false) => { + const testFn = only ? it.only : it; + testFn(`API responds successfully: ${testDescription}`, async function() { + configureUid2ApiSuccessResponse(); + await act(true); + }); + testFn(`API responds with an error: ${failTestDescription ?? testDescription}`, async function() { + configureUid2ApiFailResponse(); + await act(false); + }); +} +describe(`UID2 module`, function () { + let suiteSandbox, testSandbox, timerSpy, fullTestTitle, restoreSubtleToUndefined = false; + before(function () { + timerSpy = configureTimerInterceptors(debugOutput); + hook.ready(); + uninstallGdprEnforcement(); + + suiteSandbox = sinon.sandbox.create(); + // I'm unable to find an authoritative source, but apparently subtle isn't available in some test stacks for security reasons. + // I've confirmed it's available in Firefox since v34 (it seems to be unavailable on BrowserStack in Firefox v106). + if (typeof window.crypto.subtle === 'undefined') { + restoreSubtleToUndefined = true; + window.crypto.subtle = { importKey: () => {}, decrypt: () => {} }; + } + suiteSandbox.stub(window.crypto.subtle, 'importKey').callsFake(() => Promise.resolve()); + suiteSandbox.stub(window.crypto.subtle, 'decrypt').callsFake((settings, key, data) => Promise.resolve(new Uint8Array([...settings.iv, ...data]))); + }); + + after(function () { + suiteSandbox.restore(); + timerSpy.restore(); + if (restoreSubtleToUndefined) window.crypto.subtle = undefined; + }); + + const getFullTestTitle = (test) => `${test.parent.title ? getFullTestTitle(test.parent) + ' | ' : ''}${test.title}`; + beforeEach(function () { + debugOutput(`----------------- START TEST ------------------`); + fullTestTitle = getFullTestTitle(this.test.ctx.currentTest); + debugOutput(fullTestTitle); + testSandbox = sinon.sandbox.create(); + testSandbox.stub(utils, 'logWarn'); + + init(config); + setSubmoduleRegistry([uid2IdSubmodule]); + }); + + afterEach(async function() { + $$PREBID_GLOBAL$$.requestBids.removeAll(); + config.resetConfig(); + testSandbox.restore(); + if (timerSpy.timers.length > 0) { + if (clearTimersAfterEachTest) { + debugOutput(`Cancelling ${timerSpy.timers.length} still-active timers.`); + timerSpy.clearAllActiveTimers(); + } else { + debugOutput(`Waiting on ${timerSpy.timers.length} still-active timers...`, timerSpy.timers); + await timerSpy.waitAllActiveTimers(); + } + } + coreStorage.setCookie(moduleCookieName, '', expireCookieDate); + coreStorage.setCookie(publisherCookieName, '', expireCookieDate); + + debugOutput('----------------- END TEST ------------------'); + }); + + describe('Configuration', function() { + it('When no baseUrl is provided in config, the module calls the production endpoint', function() { + const uid2Token = makeUid2Token(initialToken, true, true); + config.setConfig(makePrebidConfig({uid2Token})); + expect(server.requests[0]?.url).to.have.string('https://prod.uidapi.com/'); + }); + + it('When a baseUrl is provided in config, the module calls the provided endpoint', function() { + const uid2Token = makeUid2Token(initialToken, true, true); + config.setConfig(makePrebidConfig({uid2Token, uid2ApiBase: 'https://operator-integ.uidapi.com'})); + expect(server.requests[0]?.url).to.have.string('https://operator-integ.uidapi.com/'); + }); + }); + + it('When a legacy value is provided directly in configuration, it is passed on', async function() { + const valueConfig = makePrebidConfig(); + valueConfig.userSync.userIds[0].value = {uid2: {id: legacyToken}} + config.setConfig(valueConfig); + const bid = await runAuction(); + + expectLegacyToken(bid); + }); + + // These tests cover 'legacy' cookies - i.e. cookies set with just the uid2 advertising token, which was how some previous integrations worked. + // Some users might still have this cookie, and the module should use that token if a newer one isn't provided. + // This should cover older integrations where the server is setting this legacy cookie and expecting the module to pass it on. + describe('When a legacy cookie exists', function () { + // Creates a test which sets the legacy cookie, configures the UID2 module with provided params, runs an + const createLegacyTest = function(params, bidAssertions) { + return async function() { + coreStorage.setCookie(moduleCookieName, legacyToken, getFutureCookieExpiry()); + config.setConfig(makePrebidConfig(params)); + + const bid = await runAuction(); + bidAssertions.forEach(function(assertion) { assertion(bid); }); + } + }; + + it('and a legacy config is used, it should provide the legacy cookie', + createLegacyTest(legacyConfigParams, [expectLegacyToken])); + it('and a server cookie config is used without a valid server cookie, it should provide the legacy cookie', + createLegacyTest(serverCookieConfigParams, [expectLegacyToken])); + it('and a server cookie is used with a valid server cookie, it should provide the server cookie', + async function() { setPublisherCookie(makeUid2Token()); await createLegacyTest(serverCookieConfigParams, [expectInitialToken, expectNoLegacyToken])(); }); + it('and a token is provided in config, it should provide the config token', + createLegacyTest({uid2Token: makeUid2Token()}, [expectInitialToken, expectNoLegacyToken])); + }); + + // This setup runs all of the functional tests with both types of config - the full token response in params, or a server cookie with the cookie name provided + let scenarios = [ + { + name: 'Token provided in config call', + setConfig: (token, extraConfig = {}) => config.setConfig(makePrebidConfig({uid2Token: token}, extraConfig)), + }, + { + name: 'Token provided in server-set cookie', + setConfig: (token, extraConfig) => { + setPublisherCookie(token); + config.setConfig(makePrebidConfig(serverCookieConfigParams, extraConfig)); + }, + } + ] + + scenarios.forEach(function(scenario) { + describe(scenario.name, function() { + describe(`When an expired token which can be refreshed is provided`, function() { + describe('When the refresh is available in time', function() { + testApiSuccessAndFailure(async function(apiSucceeds) { + scenario.setConfig(makeUid2Token(initialToken, true, true)); + respondAfterDelay(auctionDelayMs / 10); + const bid = await runAuction(); + + if (apiSucceeds) expectRefreshedToken(bid); + else expectNoIdentity(bid); + }, 'it should be used in the auction', 'the auction should have no uid2'); + + testApiSuccessAndFailure(async function(apiSucceeds) { + scenario.setConfig(makeUid2Token(initialToken, true, true)); + respondAfterDelay(auctionDelayMs / 10); + + await runAuction(); + if (apiSucceeds) { + expectModuleCookieToContain(initialToken, refreshedToken); + } else { + expectModuleCookieEmptyOrMissing(); + } + }, 'the refreshed token should be stored in the module cookie', 'the module cookie should not be set'); + }); + describe(`when the response doesn't arrive before the auction timer`, function() { + testApiSuccessAndFailure(async function() { + scenario.setConfig(makeUid2Token(initialToken, true, true)); + const bid = await runAuction(); + expectNoIdentity(bid); + }, 'it should run the auction'); + + testApiSuccessAndFailure(async function(apiSucceeds) { + scenario.setConfig(makeUid2Token(initialToken, true, true)); + const promise = respondAfterDelay(auctionDelayMs * 2); + + const bid = await runAuction(); + expectNoIdentity(bid); + expectGlobalToHaveNoUid2(); + await promise; + if (apiSucceeds) expectGlobalToHaveRefreshedIdentity(); + else expectGlobalToHaveNoUid2(); + }, 'it should update the userId after the auction', 'there should be no global identity'); + }) + describe('and there is a refreshed token in the module cookie', function() { + it('the refreshed value from the cookie is used', async function() { + const initialIdentity = makeUid2Token(initialToken, true, true); + const refreshedIdentity = makeUid2Token(refreshedToken); + const moduleCookie = {originalToken: initialIdentity, latestToken: refreshedIdentity}; + coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), getFutureCookieExpiry()); + scenario.setConfig(initialIdentity); + + const bid = await runAuction(); + expectRefreshedToken(bid); + }); + }) + }); + + describe(`When a current token is provided`, function() { + beforeEach(function() { + scenario.setConfig(makeUid2Token()); + }); + + it('it should use the token in the auction', async function() { + const bid = await runAuction(); + expectInitialToken(bid); + }); + }); + + describe(`When a current token which should be refreshed is provided, and the auction is set to run immediately`, function() { + beforeEach(function() { + scenario.setConfig(makeUid2Token(initialToken, true), {auctionDelay: 0, syncDelay: 1}); + }); + testApiSuccessAndFailure(async function() { + respondAfterDelay(10); + const bid = await runAuction(); + expectInitialToken(bid); + }, 'it should not be refreshed before the auction runs'); + + testApiSuccessAndFailure(async function(success) { + const promise = respondAfterDelay(1); + await runAuction(); + await promise; + if (success) { + expectModuleCookieToContain(initialToken, refreshedToken); + } else { + expectModuleCookieToContain(initialToken, initialToken); + } + }, 'the refreshed token should be stored in the module cookie after the auction runs', 'the module cookie should only have the original token'); + + it('it should use the current token in the auction', async function() { + const bid = await runAuction(); + expectInitialToken(bid); + }); + }); + }); + }); +}); From bc68d77cf85409c5f9f2c184a7e10bef2169d348 Mon Sep 17 00:00:00 2001 From: GlobalsunHB <119595168+GlobalsunHB@users.noreply.github.com> Date: Thu, 15 Dec 2022 15:22:55 +0200 Subject: [PATCH 527/569] Globalsun Bid Adapter: Initial Release (#9307) * init new adapter Globalsun * kick off integration tests Co-authored-by: Chris Huie --- modules/globalsunBidAdapter.js | 212 ++++++++++ modules/globalsunBidAdapter.md | 79 ++++ test/spec/modules/globalsunBidAdapter_spec.js | 398 ++++++++++++++++++ 3 files changed, 689 insertions(+) create mode 100644 modules/globalsunBidAdapter.js create mode 100644 modules/globalsunBidAdapter.md create mode 100644 test/spec/modules/globalsunBidAdapter_spec.js diff --git a/modules/globalsunBidAdapter.js b/modules/globalsunBidAdapter.js new file mode 100644 index 00000000000..ae3407bbbdd --- /dev/null +++ b/modules/globalsunBidAdapter.js @@ -0,0 +1,212 @@ +import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'globalsun'; +const AD_URL = 'https://endpoint.globalsun.io/pbjs'; +const SYNC_URL = 'https://cs.globalsun.io'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl || bid.vastXml); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); + default: + return false; + } +} + +function getPlacementReqData(bid) { + const { params, bidId, mediaTypes } = bid; + const schain = bid.schain || {}; + const { placementId, endpointId } = params; + const bidfloor = getBidFloor(bid); + + const placement = { + bidId, + schain, + bidfloor + }; + + if (placementId) { + placement.placementId = placementId; + placement.type = 'publisher'; + } else if (endpointId) { + placement.endpointId = endpointId; + placement.type = 'network'; + } + + if (mediaTypes && mediaTypes[BANNER]) { + placement.adFormat = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.adFormat = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.native = mediaTypes[NATIVE]; + placement.adFormat = NATIVE; + } + + return placement; +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (err) { + logError(err); + return 0; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid = {}) => { + const { params, bidId, mediaTypes } = bid; + let valid = Boolean(bidId && params && (params.placementId || params.endpointId)); + + if (mediaTypes && mediaTypes[BANNER]) { + valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); + } else if (mediaTypes && mediaTypes[VIDEO]) { + valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); + } else if (mediaTypes && mediaTypes[NATIVE]) { + valid = valid && Boolean(mediaTypes[NATIVE]); + } else { + valid = false; + } + return valid; + }, + + buildRequests: (validBidRequests = [], bidderRequest = {}) => { + // convert Native ORTB definition to old-style prebid native definition + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + + let deviceWidth = 0; + let deviceHeight = 0; + + let winLocation; + try { + const winTop = window.top; + deviceWidth = winTop.screen.width; + deviceHeight = winTop.screen.height; + winLocation = winTop.location; + } catch (e) { + logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + logMessage(e); + } + // TODO: does the fallback make sense here? + let location = refferLocation || winLocation; + const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + const host = location.host; + const page = location.pathname; + const secure = location.protocol === 'https:' ? 1 : 0; + const placements = []; + const request = { + deviceWidth, + deviceHeight, + language, + secure, + host, + page, + placements, + coppa: config.getConfig('coppa') === true ? 1 : 0, + ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, + tmax: config.getConfig('bidderTimeout') + }; + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + placements.push(getPlacementReqData(bid)); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/globalsunBidAdapter.md b/modules/globalsunBidAdapter.md new file mode 100644 index 00000000000..07c3ce32155 --- /dev/null +++ b/modules/globalsunBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: Globalsun Bidder Adapter +Module Type: Globalsun Bidder Adapter +Maintainer: prebid@globalsun.io +``` + +# Description + +Connects to Globalsun exchange for bids. +Globalsun bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'globalsun', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'globalsun', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'globalsun', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/globalsunBidAdapter_spec.js b/test/spec/modules/globalsunBidAdapter_spec.js new file mode 100644 index 00000000000..3795f3038a7 --- /dev/null +++ b/test/spec/modules/globalsunBidAdapter_spec.js @@ -0,0 +1,398 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/globalsunBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'globalsun' + +describe('GlobalsunBidAdapter', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative', + } + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + refererInfo: { + referer: 'https://test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://endpoint.globalsun.io/pbjs'); + }); + + it('Returns general data valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('string'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([], bidderRequest); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.globalsun.io/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1---' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.globalsun.io/image?pbjs=1&ccpa_consent=1---&coppa=0') + }); + }); +}); From 77ba4ece1dce547c3ca824d43b79e6529c843aa6 Mon Sep 17 00:00:00 2001 From: Nitin Nimbalkar <96475150+nitin0610@users.noreply.github.com> Date: Fri, 16 Dec 2022 02:50:27 +0530 Subject: [PATCH 528/569] Topics module: Initial Topics iframe implementation (#8947) * Topics: Initial Topics iframe implementation * Topics API: LINT errors solved * Added Empty Topics Check * Topics: Storage Map logic and added message listener secure check * Topics: Iframe implementation for bidders * Added topics_iframe html in example for reference * Added Pubmatic Topic iframe URL * Added Pubmatic Topic iframe URL- Removed comment * Topics Module: Consent management logic added * Topics Module: Added Device Access check * Topics Module: Unit test cases added and minor changes * Topics Module: Array.find used instead of array.some and variable name changed * Topics IFrame Implementation: Purpose present check is handled --- integrationExamples/gpt/topics_frame.html | 43 +++++ modules/topicsFpdModule.js | 183 +++++++++++++++++++++- test/spec/modules/topicsFpdModule_spec.js | 178 ++++++++++++++++++++- 3 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 integrationExamples/gpt/topics_frame.html diff --git a/integrationExamples/gpt/topics_frame.html b/integrationExamples/gpt/topics_frame.html new file mode 100644 index 00000000000..7a12b030a6a --- /dev/null +++ b/integrationExamples/gpt/topics_frame.html @@ -0,0 +1,43 @@ + + + + Topics demo + + + + + + + + + \ No newline at end of file diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js index dbd1d3e90e9..f5a3c0a2c70 100644 --- a/modules/topicsFpdModule.js +++ b/modules/topicsFpdModule.js @@ -1,8 +1,31 @@ -import {logError, logWarn, mergeDeep} from '../src/utils.js'; +import {logError, logWarn, mergeDeep, isEmpty, safeJSONParse, logInfo, hasDeviceAccess} from '../src/utils.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {submodule} from '../src/hook.js'; import {GreedyPromise} from '../src/utils/promise.js'; +import {config} from '../src/config.js'; +import {getCoreStorageManager} from '../src/storageManager.js'; +import {includes} from '../src/polyfill.js'; +import {gdprDataHandler} from '../src/adapterManager.js'; +const MODULE_NAME = 'topicsFpd'; +const DEFAULT_EXPIRATION_DAYS = 21; +const TCF_REQUIRED_PURPOSES = ['1', '2', '3', '4']; +let HAS_GDPR_CONSENT = true; +let LOAD_TOPICS_INITIALISE = false; +const HAS_DEVICE_ACCESS = hasDeviceAccess(); + +const bidderIframeList = { + maxTopicCaller: 1, + bidders: [{ + bidder: 'pubmatic', + iframeURL: 'https://ads.pubmatic.com/AdServer/js/topics/topics_frame.html' + }] +} +export const coreStorage = getCoreStorageManager(MODULE_NAME); +export const topicStorageName = 'prebid:topics'; +export const lastUpdated = 'lastUpdated'; + +const iframeLoadedURL = []; const TAXONOMIES = { // map from topic taxonomyVersion to IAB segment taxonomy '1': 600 @@ -17,6 +40,20 @@ function partitionBy(field, items) { }, {}); } +/** + * function to get list of loaded Iframes calling Topics API + */ +function getLoadedIframeURL() { + return iframeLoadedURL; +} + +/** + * function to set/push iframe in the list which is loaded to called topics API. + */ +function setLoadedIframeURL(url) { + return iframeLoadedURL.push(url); +} + export function getTopicsData(name, topics, taxonomies = TAXONOMIES) { return Object.entries(partitionBy('taxonomyVersion', topics)) .filter(([taxonomyVersion]) => { @@ -61,7 +98,12 @@ export function getTopics(doc = document) { const topicsData = getTopics().then((topics) => getTopicsData(getRefererInfo().domain, topics)); export function processFpd(config, {global}, {data = topicsData} = {}) { + if (!LOAD_TOPICS_INITIALISE) { + loadTopicsForBidders(); + LOAD_TOPICS_INITIALISE = true; + } return data.then((data) => { + data = [].concat(data, getCachedTopics()); // Add cached data in FPD data. if (data.length) { mergeDeep(global, { user: { @@ -73,6 +115,145 @@ export function processFpd(config, {global}, {data = topicsData} = {}) { }); } +/** + * function to fetch the cached topic data from storage for bidders and return it + */ +export function getCachedTopics() { + let cachedTopicData = []; + if (!HAS_GDPR_CONSENT || !HAS_DEVICE_ACCESS) { + return cachedTopicData; + } + const topics = config.getConfig('userSync.topics') || bidderIframeList; + const bidderList = topics.bidders || []; + let storedSegments = new Map(safeJSONParse(coreStorage.getDataFromLocalStorage(topicStorageName))); + storedSegments && storedSegments.forEach((value, cachedBidder) => { + // Check bidder exist in config for cached bidder data and then only retrieve the cached data + let bidderConfigObj = bidderList.find(({bidder}) => cachedBidder == bidder) + if (bidderConfigObj) { + if (!isCachedDataExpired(value[lastUpdated], bidderConfigObj?.expiry || DEFAULT_EXPIRATION_DAYS)) { + Object.keys(value).forEach((segData) => { + segData != lastUpdated && cachedTopicData.push(value[segData]); + }) + } else { + // delete the specific bidder map from the store and store the updated maps + storedSegments.delete(cachedBidder); + coreStorage.setDataInLocalStorage(topicStorageName, JSON.stringify([...storedSegments])); + } + } + }); + return cachedTopicData; +} + +/** + * Recieve messages from iframe loaded for bidders to fetch topic + * @param {MessageEvent} evt + */ +export function receiveMessage(evt) { + if (evt && evt.data) { + try { + let data = safeJSONParse(evt.data); + if (includes(getLoadedIframeURL(), evt.origin) && data && data.segment && !isEmpty(data.segment.topics)) { + const {domain, topics, bidder} = data.segment; + const iframeTopicsData = getTopicsData(domain, topics)[0]; + iframeTopicsData && storeInLocalStorage(bidder, iframeTopicsData); + } + } catch (err) { } + } +} + +/** +Function to store Topics data recieved from iframe in storage(name: "prebid:topics") +* @param {Topics} topics +*/ +export function storeInLocalStorage(bidder, topics) { + const storedSegments = new Map(safeJSONParse(coreStorage.getDataFromLocalStorage(topicStorageName))); + if (storedSegments.has(bidder)) { + storedSegments.get(bidder)[topics['ext']['segclass']] = topics; + storedSegments.get(bidder)[lastUpdated] = new Date().getTime(); + storedSegments.set(bidder, storedSegments.get(bidder)); + } else { + storedSegments.set(bidder, {[topics.ext.segclass]: topics, [lastUpdated]: new Date().getTime()}) + } + coreStorage.setDataInLocalStorage(topicStorageName, JSON.stringify([...storedSegments])); +} + +function isCachedDataExpired(storedTime, cacheTime) { + const _MS_PER_DAY = 1000 * 60 * 60 * 24; + const currentTime = new Date().getTime(); + const daysDifference = Math.ceil((currentTime - storedTime) / _MS_PER_DAY); + return daysDifference > cacheTime; +} + +/** +* Function to get random bidders based on count passed with array of bidders +**/ +function getRandomBidders(arr, count) { + return ([...arr].sort(() => 0.5 - Math.random())).slice(0, count) +} + +/** + * function to add listener for message receiving from IFRAME + */ +function listenMessagesFromTopicIframe() { + window.addEventListener('message', receiveMessage, false); +} + +function checkTCFv2(vendorData, requiredPurposes = TCF_REQUIRED_PURPOSES) { + const {gdprApplies, purpose} = vendorData; + if (!gdprApplies || !purpose) { + return true; + } + return requiredPurposes.map((purposeNo) => { + const purposeConsent = purpose.consents ? purpose.consents[purposeNo] : false; + if (purposeConsent) { + return true; + } + return false; + }).reduce((a, b) => a && b, true); +} + +export function hasGDPRConsent() { + // Check for GDPR consent for purpose 1,2,3,4 and return false if consent has not been given + const gdprConsent = gdprDataHandler.getConsentData(); + const hasGdpr = (gdprConsent && typeof gdprConsent.gdprApplies === 'boolean' && gdprConsent.gdprApplies) ? 1 : 0; + const gdprConsentString = hasGdpr ? gdprConsent.consentString : ''; + if (hasGdpr) { + if ((!gdprConsentString || gdprConsentString === '') || !gdprConsent.vendorData) { + return false; + } + return checkTCFv2(gdprConsent.vendorData); + } + return true; +} + +/** + * function to load the iframes of the bidder to load the topics data + */ +function loadTopicsForBidders() { + HAS_GDPR_CONSENT = hasGDPRConsent(); + if (!HAS_GDPR_CONSENT || !HAS_DEVICE_ACCESS) { + logInfo('Topics Module : Consent string is required to fetch the topics from third party domains.'); + return; + } + const topics = config.getConfig('userSync.topics') || bidderIframeList; + if (topics) { + listenMessagesFromTopicIframe(); + const randomBidders = getRandomBidders(topics.bidders || [], topics.maxTopicCaller || 1) + randomBidders && randomBidders.forEach(({ bidder, iframeURL }) => { + if (bidder && iframeURL) { + let ifrm = document.createElement('iframe'); + ifrm.name = 'ifrm_'.concat(bidder); + ifrm.src = ''.concat(iframeURL, '?bidder=').concat(bidder); + ifrm.style.display = 'none'; + setLoadedIframeURL(new URL(iframeURL).origin); + iframeURL && window.document.documentElement.appendChild(ifrm); + } + }) + } else { + logWarn(`Topics config not defined under userSync Object`); + } +} + submodule('firstPartyData', { name: 'topics', queue: 1, diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js index 3781768497b..69e7cc16ca8 100644 --- a/test/spec/modules/topicsFpdModule_spec.js +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -1,5 +1,7 @@ -import {getTopics, getTopicsData, processFpd} from '../../../modules/topicsFpdModule.js'; -import {deepClone} from '../../../src/utils.js'; +import {getTopics, getTopicsData, processFpd, hasGDPRConsent, getCachedTopics, receiveMessage, topicStorageName} from '../../../modules/topicsFpdModule.js'; +import {deepClone, safeJSONParse} from '../../../src/utils.js'; +import {gdprDataHandler} from 'src/adapterManager.js'; +import {getCoreStorageManager} from 'src/storageManager.js'; describe('getTopicsData', () => { function makeTopic(topic, modelv, taxv = '1') { @@ -237,3 +239,175 @@ describe('processFpd', () => { }); }); }); + +describe('Topics Module GDPR consent check', () => { + let gdprDataHdlrStub; + beforeEach(() => { + gdprDataHdlrStub = sinon.stub(gdprDataHandler, 'getConsentData'); + }) + + afterEach(() => { + gdprDataHdlrStub.restore(); + }); + + it('should return false when GDPR is applied but consent string is not present', () => { + const consentString = ''; + const consentConfig = { + consentString: consentString, + gdprApplies: true, + vendorData: {} + }; + gdprDataHdlrStub.returns(consentConfig); + expect(hasGDPRConsent()).to.equal(false); + }); + + it("should return true when GDPR doesn't apply", () => { + const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; + const consentConfig = { + consentString: consentString, + gdprApplies: false, + vendorData: {} + }; + + gdprDataHdlrStub.returns(consentConfig); + expect(hasGDPRConsent()).to.equal(true); + }); + + it('should return true when GDPR is applied and purpose consent is true for all purpose[1,2,3,4]', () => { + const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; + const consentConfig = { + consentString: consentString, + gdprApplies: true, + vendorData: { + metadata: consentString, + gdprApplies: true, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true + } + } + } + }; + + gdprDataHdlrStub.returns(consentConfig); + expect(hasGDPRConsent()).to.equal(true); + }); + + it('should return false when GDPR is applied and purpose consent is false for one of the purpose[1,2,3,4]', () => { + const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; + const consentConfig = { + consentString: consentString, + gdprApplies: true, + vendorData: { + metadata: consentString, + gdprApplies: true, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: false + } + } + } + }; + + gdprDataHdlrStub.returns(consentConfig); + expect(hasGDPRConsent()).to.equal(false); + }); +}); + +describe('getCachedTopics()', () => { + const storage = getCoreStorageManager('topicsFpd'); + const expected = [{ + ext: { + segtax: 600, + segclass: '2206021246' + }, + segment: [{ + 'id': '243' + }, { + 'id': '265' + }], + name: 'ads.pubmatic.com' + }]; + const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; + const consentConfig = { + consentString: consentString, + gdprApplies: true, + vendorData: { + metadata: consentString, + gdprApplies: true, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true + } + } + } + }; + const mockData = [ + { + name: 'domain', + segment: [{id: 123}] + }, + { + name: 'domain', + segment: [{id: 321}], + } + ]; + + const evt = { + data: '{"segment":{"domain":"ads.pubmatic.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"pubmatic"},"date":1669743901858}', + origin: 'https://ads.pubmatic.com' + } + + let gdprDataHdlrStub; + beforeEach(() => { + gdprDataHdlrStub = sinon.stub(gdprDataHandler, 'getConsentData'); + }); + + afterEach(() => { + storage.removeDataFromLocalStorage(topicStorageName); + gdprDataHdlrStub.restore(); + }); + + it('should return segments for bidder if GDPR consent is true and there is cached segments stored which is not expired', () => { + let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1669719242027}]]'; + storage.setDataInLocalStorage(topicStorageName, storedSegments); + gdprDataHdlrStub.returns(consentConfig); + assert.deepEqual(getCachedTopics(), expected); + }); + + it('should return empty segments for bidder if GDPR consent is true and there is cached segments stored which is expired', () => { + let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1659719242027}]]'; + storage.setDataInLocalStorage(topicStorageName, storedSegments); + gdprDataHdlrStub.returns(consentConfig); + assert.deepEqual(getCachedTopics(), []); + }); + + it('should stored segments if receiveMessage event is triggerred with segment data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) + .then(({global}) => { + receiveMessage(evt) + let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); + expect(segments.has('pubmatic')).to.equal(true); + }); + }); + + it('should update stored segments if receiveMessage event is triggerred with segment data', () => { + let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1669719242027}]]'; + storage.setDataInLocalStorage(topicStorageName, storedSegments); + return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) + .then(({global}) => { + receiveMessage(evt); + let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); + expect(segments.get('pubmatic')[2206021246].segment.length).to.equal(1); + }); + }); +}) From bd4a6703b43f25168865ef6eea18efbabb9bda8f Mon Sep 17 00:00:00 2001 From: jsfledd Date: Fri, 16 Dec 2022 03:35:56 -0800 Subject: [PATCH 529/569] Nativo Bid Adapter: added ntv_url qs param value validation (#9334) * Initial nativoBidAdapter document creation (js, md and spec) * Fulling working prebid using nativoBidAdapter. Support for GDPR and CCPA in user syncs. * Added defult size settings based on the largest ad unit. Added response body validation. Added consent to request url qs params. * Changed bidder endpoint url * Changed double quotes to single quotes. * Reverted package-json.lock to remove modifications from PR * Added optional bidder param 'url' so the ad server can force- match an existing placement * Lint fix. Added space after if. * Added new QS param to send various adUnit data to adapter endpopint * Updated unit test for new QS param * Added qs param to keep track of ad unit refreshes * Updated bidMap key default value * Updated refresh increment logic * Refactored spread operator for IE11 support * Updated isBidRequestValid check * Refactored Object.enties to use Object.keys to fix CircleCI testing errors * Updated bid mapping key creation to prioritize ad unit code over placementId * Added filtering by ad, advertiser and campaign. * Merged master * Added more robust bidDataMap with multiple key access * Deduped filer values * Rolled back package.json * Duped upstream/master's package.lock file ... not sure how it got changed in the first place * Small refactor of filterData length check. Removed comparison with 0 since a length value of 0 is already falsy. * Added bid sizes to request * Fixed function name in spec. Added unit tests. * Added priceFloor module support * Added protection agains empty url parameter * Changed ntv_url QS param to use referrer.location instead of referrer.page * Removed testing 'only' flag * Added ntv_url QS param value validation --- modules/nativoBidAdapter.js | 56 +++++++++++---- test/spec/modules/nativoBidAdapter_spec.js | 84 ++++++++++++++++++++++ 2 files changed, 128 insertions(+), 12 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 7eb37ea8b82..a92168492d0 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -140,13 +140,9 @@ export const spec = { const floorPriceData = {} let placementId, pageUrl validBidRequests.forEach((bidRequest) => { - pageUrl = deepAccess( - bidRequest, - 'params.url', - ) - if (pageUrl == undefined || pageUrl === '') { - pageUrl = bidderRequest.refererInfo.location - } + pageUrl = + getPageUrlFromBidRequest(bidRequest) || + bidderRequest.refererInfo.location placementId = deepAccess(bidRequest, 'params.placementId') @@ -380,10 +376,12 @@ export const spec = { return syncs } - body = - typeof response.body === 'string' - ? JSON.parse(response.body) - : response.body + try { + body = + typeof response.body === 'string' + ? JSON.parse(response.body) + : response.body + } catch (err) { return } // Make sure we have valid content if (!body || !body.seatbid || body.seatbid.length === 0) return @@ -465,7 +463,7 @@ export function parseFloorPriceData(bidRequest) { // Setup price floor data per media type let mediaTypeData = bidMediaTypes[mediaType] let mediaTypeFloorPriceData = {} - let mediaTypeSizes = mediaTypeData.sizes || mediaTypeData.playerSize || []; + let mediaTypeSizes = mediaTypeData.sizes || mediaTypeData.playerSize || [] // Step through each size of the media type so we can get floor data for each size per media type mediaTypeSizes.forEach((size) => { // Get floor price data per the getFloor method and respective media type / size combination @@ -634,3 +632,37 @@ function appendFilterData(filter, filterData) { filterData.forEach((ad) => filter.add(ad)) } } + +export function getPageUrlFromBidRequest(bidRequest) { + let paramPageUrl = deepAccess(bidRequest, 'params.url') + + if (paramPageUrl == undefined) return + + if (hasProtocol(paramPageUrl)) return paramPageUrl + + paramPageUrl = addProtocol(paramPageUrl) + + try { + const url = new URL(paramPageUrl) + return url.href + } catch (err) {} +} + +export function hasProtocol(url) { + const protocolRegexp = /^http[s]?\:/ + return protocolRegexp.test(url) +} + +export function addProtocol(url) { + if (hasProtocol(url)) { + return url + } + + let protocolPrefix = 'https:' + + if (url.indexOf('//') !== 0) { + protocolPrefix += '//' + } + + return `${protocolPrefix}${url}` +} diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 4cd4c92281b..4d70e6f7071 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -5,6 +5,9 @@ import { getMediaWildcardPrices, sizeToString, parseFloorPriceData, + getPageUrlFromBidRequest, + hasProtocol, + addProtocol, } from '../../../modules/nativoBidAdapter' describe('bidDataMap', function () { @@ -647,3 +650,84 @@ describe('parseFloorPriceData', () => { }) }) }) + +describe('hasProtocol', () => { + it('https://www.testpage.com', () => { + expect(hasProtocol('https://www.testpage.com')).to.be.true + }) + it('http://www.testpage.com', () => { + expect(hasProtocol('http://www.testpage.com')).to.be.true + }) + it('//www.testpage.com', () => { + expect(hasProtocol('//www.testpage.com')).to.be.false + }) + it('www.testpage.com', () => { + expect(hasProtocol('www.testpage.com')).to.be.false + }) + it('httpsgsjhgflih', () => { + expect(hasProtocol('httpsgsjhgflih')).to.be.false + }) +}) + +describe('addProtocol', () => { + it('www.testpage.com', () => { + expect(addProtocol('www.testpage.com')).to.be.equal('https://www.testpage.com') + }) + it('//www.testpage.com', () => { + expect(addProtocol('//www.testpage.com')).to.be.equal('https://www.testpage.com') + }) + it('http://www.testpage.com', () => { + expect(addProtocol('http://www.testpage.com')).to.be.equal('http://www.testpage.com') + }) + it('https://www.testpage.com', () => { + expect(addProtocol('https://www.testpage.com')).to.be.equal('https://www.testpage.com') + }) +}) + +describe('getPageUrlFromBidRequest', () => { + const bidRequest = {} + + beforeEach(() => { + bidRequest.params = {} + }) + + it('Returns undefined for no url param', () => { + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).to.be.undefined + }) + + it('@testUrl', () => { + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).to.be.undefined + }) + + it('https://www.testpage.com', () => { + bidRequest.params.url = 'https://www.testpage.com' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) + + it('https://www.testpage.com/test/path', () => { + bidRequest.params.url = 'https://www.testpage.com/test/path' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) + + it('www.testpage.com', () => { + bidRequest.params.url = 'www.testpage.com' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) + + it('http://www.testpage.com', () => { + bidRequest.params.url = 'http://www.testpage.com' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) + + it('//www.testpage.com', () => { + bidRequest.params.url = '//www.testpage.com' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) +}) From 75d0c737d6493f49f19cdb9c1cccc0fee30cee15 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 16 Dec 2022 15:01:38 +0000 Subject: [PATCH 530/569] Prebid 7.29.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 006b8bcdcab..f5bf3feb080 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.29.0-pre", + "version": "7.29.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 5ee4cb728b1..acda15c2d0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.29.0-pre", + "version": "7.29.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 37259fc9d8fead5e9d0783202f31f96e32c49e2d Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 16 Dec 2022 15:01:38 +0000 Subject: [PATCH 531/569] Increment version to 7.30.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5bf3feb080..c903795e59d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.29.0", + "version": "7.30.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index acda15c2d0e..490303874f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.29.0", + "version": "7.30.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a531b20b5d6a72ab9e299c1d73a2e9cf959a5abd Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 19 Dec 2022 15:01:09 -0700 Subject: [PATCH 532/569] Build system: set up `hook` for tests (#9350) --- karma.conf.maker.js | 2 +- test/helpers/hookSetup.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 test/helpers/hookSetup.js diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 692db7755e8..e05d5b08afd 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -110,7 +110,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file, disableFe var webpackConfig = newWebpackConfig(codeCoverage, disableFeatures); var plugins = newPluginsArray(browserstack); - var files = file ? ['test/test_deps.js', file].flatMap(f => f) : ['test/test_index.js']; + var files = file ? ['test/test_deps.js', file, 'test/helpers/hookSetup.js'].flatMap(f => f) : ['test/test_index.js']; // This file opens the /debug.html tab automatically. // It has no real value unless you're running --watch, and intend to do some debugging in the browser. if (watchMode) { diff --git a/test/helpers/hookSetup.js b/test/helpers/hookSetup.js new file mode 100644 index 00000000000..2de35bb1dd4 --- /dev/null +++ b/test/helpers/hookSetup.js @@ -0,0 +1,5 @@ +import {hook} from '../../src/hook.js'; + +before(() => { + hook.ready(); +}); From 33d6acd784eee9c632396841b94694d191095784 Mon Sep 17 00:00:00 2001 From: Vic R <103455651+victorlassomarketing@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:03:34 -0800 Subject: [PATCH 533/569] add encoding for device param (#9352) --- modules/lassoBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/lassoBidAdapter.js b/modules/lassoBidAdapter.js index fafaa4dd9dc..ad25cbe1e85 100644 --- a/modules/lassoBidAdapter.js +++ b/modules/lassoBidAdapter.js @@ -40,14 +40,14 @@ export const spec = { auctionId: bidRequest.auctionId, bidId: bidRequest.bidId, transactionId: bidRequest.transactionId, - device: JSON.stringify(getDeviceData()), + device: encodeURIComponent(JSON.stringify(getDeviceData())), sizes, aimXR, uid: '$UID', params: JSON.stringify(bidRequest.params), crumbs: JSON.stringify(bidRequest.crumbs), prebidVersion: '$prebid.version$', - version: 2, + version: 3, coppa: config.getConfig('coppa') == true ? 1 : 0, ccpa: bidderRequest.uspConsent || undefined } From 40450ab7b30fd983356d00c7369acf0c53711380 Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Tue, 20 Dec 2022 00:11:00 +0100 Subject: [PATCH 534/569] OneTag Bid Adapter: add use of refererInfo Prebid object and Network API (#9306) * OneTag Bid Adapter: add use of refererInfo Prebid object and Network Information API * Replace refererInfo.location with refererInfo.page Co-authored-by: federico --- modules/onetagBidAdapter.js | 49 +++++++++----------- test/spec/modules/onetagBidAdapter_spec.js | 52 ++++++++++++---------- 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 40ef6d596be..b5217e77cd6 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -3,7 +3,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { INSTREAM, OUTSTREAM } from '../src/video.js'; import { Renderer } from '../src/Renderer.js'; -import {find} from '../src/polyfill.js'; +import { find } from '../src/polyfill.js'; import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { deepClone, logError, deepAccess } from '../src/utils.js'; @@ -13,7 +13,7 @@ const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; const BIDDER_CODE = 'onetag'; const GVLID = 241; -const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ gvlid: GVLID, bidderCode: BIDDER_CODE }); /** * Determines whether or not the given bid request is valid. @@ -53,7 +53,7 @@ export function isValid(type, bid) { function buildRequests(validBidRequests, bidderRequest) { const payload = { bids: requestsToBids(validBidRequests), - ...getPageInfo() + ...getPageInfo(bidderRequest) }; if (bidderRequest && bidderRequest.gdprConsent) { payload.gdprConsent = { @@ -74,7 +74,10 @@ function buildRequests(validBidRequests, bidderRequest) { if (storage.hasLocalStorage()) { payload.onetagSid = storage.getDataFromLocalStorage('onetag_sid'); } - } catch (e) {} + } catch (e) { } + const connection = navigator.connection || navigator.webkitConnection; + payload.networkConnectionType = (connection && connection.type) ? connection.type : null; + payload.networkEffectiveConnectionType = (connection && connection.effectiveType) ? connection.effectiveType : null; return { method: 'POST', url: ENDPOINT, @@ -112,7 +115,7 @@ function interpretResponse(serverResponse, bidderRequest) { if (bid.mediaType === BANNER) { responseBid.ad = bid.ad; } else if (bid.mediaType === VIDEO) { - const {context, adUnitCode} = find(requestData.bids, (item) => + const { context, adUnitCode } = find(requestData.bids, (item) => item.bidId === bid.requestId && item.type === VIDEO ); @@ -141,7 +144,7 @@ function createRenderer(bid, rendererOptions = {}) { loaded: false }); try { - renderer.setRender(({renderer, width, height, vastXml, adUnitCode}) => { + renderer.setRender(({ renderer, width, height, vastXml, adUnitCode }) => { renderer.push(() => { window.onetag.Player.init({ ...bid, @@ -162,7 +165,6 @@ function createRenderer(bid, rendererOptions = {}) { function getFrameNesting() { let topmostFrame = window; let parent = window.parent; - let currentFrameNesting = 0; try { while (topmostFrame !== topmostFrame.parent) { parent = topmostFrame.parent; @@ -170,13 +172,8 @@ function getFrameNesting() { parent.location.href; topmostFrame = topmostFrame.parent; } - } catch (e) { - currentFrameNesting = parent === topmostFrame.top ? 1 : 2; - } - return { - topmostFrame, - currentFrameNesting - } + } catch (e) { } + return topmostFrame; } function getDocumentVisibility(window) { @@ -197,21 +194,15 @@ function getDocumentVisibility(window) { /** * Returns information about the page needed by the server in an object to be converted in JSON - * @returns {{location: *, referrer: (*|string), masked: *, wWidth: (*|Number), wHeight: (*|Number), sWidth, sHeight, date: string, timeOffset: number}} + * @returns {{location: *, referrer: (*|string), stack: (*|Array.), numIframes: (*|Number), wWidth: (*|Number), wHeight: (*|Number), sWidth, sHeight, date: string, timeOffset: number}} */ -function getPageInfo() { - const { topmostFrame, currentFrameNesting } = getFrameNesting(); +function getPageInfo(bidderRequest) { + const topmostFrame = getFrameNesting(); return { - location: topmostFrame.location.href, - referrer: - topmostFrame.document.referrer !== '' - ? topmostFrame.document.referrer - : null, - ancestorOrigin: - window.location.ancestorOrigins && window.location.ancestorOrigins.length > 0 - ? window.location.ancestorOrigins[window.location.ancestorOrigins.length - 1] - : null, - masked: currentFrameNesting, + location: deepAccess(bidderRequest, 'refererInfo.page', null), + referrer: deepAccess(bidderRequest, 'refererInfo.ref', null), + stack: deepAccess(bidderRequest, 'refererInfo.stack', []), + numIframes: deepAccess(bidderRequest, 'refererInfo.numIframes', 0), wWidth: topmostFrame.innerWidth, wHeight: topmostFrame.innerHeight, oWidth: topmostFrame.outerWidth, @@ -230,7 +221,7 @@ function getPageInfo() { timing: getTiming(), version: { prebid: '$prebid.version$', - adapter: '1.1.0' + adapter: '1.1.1' } }; } @@ -344,7 +335,7 @@ function getSizes(sizes) { const ret = []; for (let i = 0; i < sizes.length; i++) { const size = sizes[i]; - ret.push({width: size[0], height: size[1]}) + ret.push({ width: size[0], height: size[1] }) } return ret; } diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 2dc0a43bbb0..71e897c7f9e 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -1,8 +1,8 @@ import { spec, isValid, hasTypeVideo, isSchainValid } from 'modules/onetagBidAdapter.js'; import { expect } from 'chai'; -import {find} from 'src/polyfill.js'; +import { find } from 'src/polyfill.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; -import {INSTREAM, OUTSTREAM} from 'src/video.js'; +import { INSTREAM, OUTSTREAM } from 'src/video.js'; describe('onetag', function () { function createBid() { @@ -122,14 +122,14 @@ describe('onetag', function () { it('Should return true when correct multi format bid is passed', function () { expect(spec.isBidRequestValid(createMultiFormatBid())).to.be.true; }); - it('Should split multi format bid into two single format bid with same bidId', function() { - const bids = JSON.parse(spec.buildRequests([ createMultiFormatBid() ]).data).bids; + it('Should split multi format bid into two single format bid with same bidId', function () { + const bids = JSON.parse(spec.buildRequests([createMultiFormatBid()]).data).bids; expect(bids.length).to.equal(2); expect(bids[0].bidId).to.equal(bids[1].bidId); }); - it('Should retrieve correct request bid when extracting video request data', function() { + it('Should retrieve correct request bid when extracting video request data', function () { const requestBid = createMultiFormatBid(); - const multiFormatRequest = spec.buildRequests([ requestBid ]); + const multiFormatRequest = spec.buildRequests([requestBid]); const serverResponse = { body: { bids: [ @@ -173,24 +173,30 @@ describe('onetag', function () { const data = JSON.parse(d); it('Should contain all keys', function () { expect(data).to.be.an('object'); - expect(data).to.include.all.keys('location', 'referrer', 'masked', 'sHeight', 'sWidth', 'docHeight', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids', 'docHidden', 'xOffset', 'yOffset', 'timing', 'version'); - expect(data.location).to.be.a('string'); - expect(data.masked).to.be.oneOf([0, 1, 2]); + expect(data).to.include.all.keys('location', 'referrer', 'stack', 'numIframes', 'sHeight', 'sWidth', 'docHeight', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids', 'docHidden', 'xOffset', 'yOffset', 'networkConnectionType', 'networkEffectiveConnectionType', 'timing', 'version'); + expect(data.location).to.satisfy(function (value) { + return value === null || typeof value === 'string'; + }); expect(data.referrer).to.satisfy(referrer => referrer === null || typeof referrer === 'string'); + expect(data.stack).to.be.an('array'); + expect(data.numIframes).to.be.a('number'); expect(data.sHeight).to.be.a('number'); expect(data.sWidth).to.be.a('number'); expect(data.wWidth).to.be.a('number'); expect(data.wHeight).to.be.a('number'); expect(data.oHeight).to.be.a('number'); expect(data.oWidth).to.be.a('number'); - expect(data.ancestorOrigin).to.satisfy(function (value) { - return value === null || typeof value === 'string'; - }); expect(data.aWidth).to.be.a('number'); expect(data.aHeight).to.be.a('number'); expect(data.sLeft).to.be.a('number'); expect(data.sTop).to.be.a('number'); expect(data.hLength).to.be.a('number'); + expect(data.networkConnectionType).to.satisfy(function (value) { + return value === null || typeof value === 'string' + }); + expect(data.networkEffectiveConnectionType).to.satisfy(function (value) { + return value === null || typeof value === 'string' + }); expect(data.bids).to.be.an('array'); expect(data.version).to.have.all.keys('prebid', 'adapter'); const bids = data['bids']; @@ -231,14 +237,14 @@ describe('onetag', function () { expect(bid.pubId).to.be.a('string'); } }); - } catch (e) {} + } catch (e) { } it('Returns empty data if no valid requests are passed', function () { serverRequest = spec.buildRequests([]); let dataString = serverRequest.data; try { let dataObj = JSON.parse(dataString); expect(dataObj.bids).to.be.an('array').that.is.empty; - } catch (e) {} + } catch (e) { } }); it('should send GDPR consent data', function () { let consentString = 'consentString'; @@ -287,7 +293,7 @@ describe('onetag', function () { let dataItem = interpretedResponse[i]; expect(dataItem).to.include.all.keys('requestId', 'cpm', 'width', 'height', 'ttl', 'creativeId', 'netRevenue', 'currency', 'meta', 'dealId'); if (dataItem.meta.mediaType === VIDEO) { - const {context} = find(requestData.bids, (item) => item.bidId === dataItem.requestId); + const { context } = find(requestData.bids, (item) => item.bidId === dataItem.requestId); if (context === INSTREAM) { expect(dataItem).to.include.all.keys('videoCacheKey', 'vastUrl'); expect(dataItem.vastUrl).to.be.a('string'); @@ -321,7 +327,7 @@ describe('onetag', function () { describe('getUserSyncs', function () { const sync_endpoint = 'https://onetag-sys.com/usync/'; it('Returns an iframe if iframeEnabled is true', function () { - const syncs = spec.getUserSyncs({iframeEnabled: true}); + const syncs = spec.getUserSyncs({ iframeEnabled: true }); expect(syncs).to.be.an('array'); expect(syncs.length).to.equal(1); expect(syncs[0].type).to.equal('iframe'); @@ -383,16 +389,16 @@ describe('onetag', function () { expect(isSchainValid(undefined)).to.be.false; }); it('Should return false when schain is missing nodes key', function () { - const schain = {'otherKey': 'otherValue'}; + const schain = { 'otherKey': 'otherValue' }; expect(isSchainValid(schain)).to.be.false; }); it('Should return false when schain is missing one of the required SupplyChainNode attribute', function () { - const missingAsiNode = {'sid': '00001', 'hp': 1}; - const missingSidNode = {'asi': 'indirectseller.com', 'hp': 1}; - const missingHpNode = {'asi': 'indirectseller.com', 'sid': '00001'}; - expect(isSchainValid({'config': {'nodes': [missingAsiNode]}})).to.be.false; - expect(isSchainValid({'config': {'nodes': [missingSidNode]}})).to.be.false; - expect(isSchainValid({'config': {'nodes': [missingHpNode]}})).to.be.false; + const missingAsiNode = { 'sid': '00001', 'hp': 1 }; + const missingSidNode = { 'asi': 'indirectseller.com', 'hp': 1 }; + const missingHpNode = { 'asi': 'indirectseller.com', 'sid': '00001' }; + expect(isSchainValid({ 'config': { 'nodes': [missingAsiNode] } })).to.be.false; + expect(isSchainValid({ 'config': { 'nodes': [missingSidNode] } })).to.be.false; + expect(isSchainValid({ 'config': { 'nodes': [missingHpNode] } })).to.be.false; }); it('Should return true when schain contains all required attributes', function () { const validSchain = { From c56d78a2cca3ef46a2cb7a33fec40b35545d8b0a Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:11:56 +0300 Subject: [PATCH 535/569] TheMediaGrid: fix tmax value (#9339) --- modules/gridBidAdapter.js | 3 +-- test/spec/modules/gridBidAdapter_spec.js | 18 ------------------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index b300c5c58c0..aa6c0ab668f 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -94,8 +94,7 @@ export const spec = { let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest || {}; const referer = refererInfo ? encodeURIComponent(refererInfo.page) : ''; - const bidderTimeout = config.getConfig('bidderTimeout') || timeout; - const tmax = timeout ? Math.min(bidderTimeout, timeout) : bidderTimeout; + const tmax = timeout || config.getConfig('bidderTimeout'); const imp = []; const bidsMap = {}; const requests = []; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 4b93c287fee..d411f33ab50 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -840,24 +840,6 @@ describe('TheMediaGrid Adapter', function () { expect(payload.site.content.id).to.equal(contentId); }); - it('should be right tmax when timeout in config is less then timeout in bidderRequest', function() { - const getConfigStub = sinon.stub(config, 'getConfig').callsFake( - arg => arg === 'bidderTimeout' ? 2000 : null); - const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload.tmax).to.equal(2000); - getConfigStub.restore(); - }); - it('should be right tmax when timeout in bidderRequest is less then timeout in config', function() { - const getConfigStub = sinon.stub(config, 'getConfig').callsFake( - arg => arg === 'bidderTimeout' ? 5000 : null); - const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload.tmax).to.equal(3000); - getConfigStub.restore(); - }); it('should contain regs.coppa if coppa is true in config', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'coppa' ? true : null); From 5950984cf2aebdc1ea0f18c14ee057ddd79f1df7 Mon Sep 17 00:00:00 2001 From: geoffray-viously <95097046+geoffray-viously@users.noreply.github.com> Date: Wed, 21 Dec 2022 06:32:14 +0100 Subject: [PATCH 536/569] Viously Bid Adapter : New Adapter (#9076) * Add viously Bid Adapter * Mod: viously documentation * MR fixes --- modules/viouslyBidAdapter.js | 209 +++++++++++ modules/viouslyBidAdapter.md | 35 ++ test/spec/modules/ViouslyBidAdapter_spec.js | 378 ++++++++++++++++++++ 3 files changed, 622 insertions(+) create mode 100644 modules/viouslyBidAdapter.js create mode 100644 modules/viouslyBidAdapter.md create mode 100644 test/spec/modules/ViouslyBidAdapter_spec.js diff --git a/modules/viouslyBidAdapter.js b/modules/viouslyBidAdapter.js new file mode 100644 index 00000000000..f18c9c7b3db --- /dev/null +++ b/modules/viouslyBidAdapter.js @@ -0,0 +1,209 @@ +import { deepAccess, logError, parseUrl, parseSizesInput, triggerPixel } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { VIDEO } from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; // eslint-disable-line prebid/validate-imports + +const BIDDER_CODE = 'viously'; +// const GVLID = 1028; +const CURRENCY = 'EUR'; +const TTL = 60; +const HTTP_METHOD = 'POST'; +const REQUEST_URL = 'https://bidder.viously.com/bid'; +const REQUIRED_VIDEO_PARAMS = ['context', 'playbackmethod', 'playerSize']; +const REQUIRED_VIOUSLY_PARAMS = ['pid']; + +export const spec = { + code: BIDDER_CODE, + // gvlid: GVLID, + supportedMediaTypes: [VIDEO], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + let videoParams = deepAccess(bid, 'mediaTypes.video'); + + if (!bid.params) { + logError('The bid params are missing'); + return false; + } + + if (!videoParams) { + logError('The placement must be of video type'); + return false; + } + + /** + * VIDEO checks + */ + + let areParamsValid = true; + + REQUIRED_VIDEO_PARAMS.forEach(function(videoParam) { + if (typeof videoParams[videoParam] === 'undefined') { + logError('mediaTypes.video.' + videoParam + ' must be set for video placement.'); + areParamsValid = false; + } + }); + + if (parseSizesInput(videoParams.playerSize).length === 0) { + logError('mediaTypes.video.playerSize must be set for video placement at the right format.'); + return false; + } + + /** + * Viously checks + */ + + REQUIRED_VIOUSLY_PARAMS.forEach(function(viouslyParam) { + if (typeof bid.params[viouslyParam] === 'undefined') { + logError('The ' + viouslyParam + ' is missing.'); + areParamsValid = false; + } + }); + + if (!areParamsValid) { + return false; + } + + return true; + }, + + buildRequests: function(validBidRequests, bidderRequest) { + let payload = {}; + + /** Viously Publisher ID */ + if (validBidRequests[0].params.pid) { + payload.pid = validBidRequests[0].params.pid; + } + + // Referer Info + if (config.getConfig('pageUrl')) { + let parsedUrl = parseUrl(config.getConfig('pageUrl')); + + payload.domain = parsedUrl.hostname; + payload.page_domain = config.getConfig('pageUrl'); + } else if (bidderRequest && bidderRequest.refererInfo) { + let parsedUrl = parseUrl(bidderRequest.refererInfo.page); + + payload.domain = parsedUrl.hostname; + payload.page_domain = bidderRequest.refererInfo.page; + } + if (payload.domain) { + /** Make sur that the scheme is not part of the domain */ + payload.domain = payload.domain.replace(/(^\w+:|^)\/\//, ''); + payload.domain = payload.domain.replace(/\/$/, ''); + } + + // Handle GDPR + if (bidderRequest && bidderRequest.gdprConsent) { + payload.gdpr = bidderRequest.gdprConsent.gdprApplies; + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { + payload.addtl_consent = bidderRequest.gdprConsent.addtlConsent; + } + } + + // US Privacy + if (bidderRequest && bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; + } + + // Schain + if (validBidRequests[0].schain) { + payload.schain = validBidRequests[0].schain; + } + // Currency + payload.currency_code = CURRENCY; + + // User IDs + if (validBidRequests[0].userIdAsEids) { + payload.users_uid = validBidRequests[0].userIdAsEids; + } + + // Placements + payload.placements = validBidRequests.map(bidRequest => { + let request = { + id: bidRequest.adUnitCode, + bid_id: bidRequest.bidId + }; + + request.video_params = { + context: deepAccess(bidRequest, 'mediaTypes.video.context'), + playbackmethod: deepAccess(bidRequest, 'mediaTypes.video.playbackmethod'), + size: parseSizesInput(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) + }; + + return request; + }); + + return { + method: HTTP_METHOD, + url: validBidRequests[0].params.endpoint ? validBidRequests[0].params.endpoint : REQUEST_URL, + data: payload + }; + }, + + interpretResponse: function(serverResponse, requests) { + const bidResponses = []; + const responseBody = serverResponse.body; + + if (responseBody.ads && responseBody.ads.length > 0) { + responseBody.ads.forEach(function(bidResponse) { + if (bidResponse.bid) { + let bidRequest = find(requests.data.placements, bid => bid.bid_id === bidResponse.bid_id); + + if (bidRequest) { + let sizes = bidResponse.size.split('x'); + + const bid = { + requestId: bidRequest.bid_id, + id: bidResponse.id, + cpm: bidResponse.cpm, + width: sizes[0], + height: sizes[1], + creativeId: bidResponse.creative_id || '', + currency: CURRENCY, + netRevenue: true, + ttl: TTL, + mediaType: 'video', + meta: {}, + // Tracking data + nurl: bidResponse.nurl ? bidResponse.nurl : [] + }; + + if (bidResponse.ad_url) { + bid.vastUrl = bidResponse.ad_url; + } else { + bid.vastXml = bidResponse.ad; + } + + bidResponses.push(bid); + } + } + }); + } + + return bidResponses; + }, + + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {}, + + onTimeout: function(timeoutData) {}, + + onBidWon: function(bid) { + if (bid && bid.nurl && bid.nurl.length > 0) { + bid.nurl.forEach(function(winUrl) { + triggerPixel(winUrl, null); + }); + } + }, + + onSetTargeting: function(bid) {} +}; + +registerBidder(spec); diff --git a/modules/viouslyBidAdapter.md b/modules/viouslyBidAdapter.md new file mode 100644 index 00000000000..0d15525cc72 --- /dev/null +++ b/modules/viouslyBidAdapter.md @@ -0,0 +1,35 @@ +# Overview + +``` +Module Name: Viously Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@viously.com +``` + +# Description + +Module that connects to Viously's demand sources + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + video: { + playerSize: [640, 360], + context: 'instream', + playbackmethod: [1, 2, 3, 4, 5, 6] + } + }, + bids: [ + { + bidder: 'viously', + params: { + pid: '20d30b78-43ec-11ed-b878-0242ac120002' + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/ViouslyBidAdapter_spec.js b/test/spec/modules/ViouslyBidAdapter_spec.js new file mode 100644 index 00000000000..612c5f87138 --- /dev/null +++ b/test/spec/modules/ViouslyBidAdapter_spec.js @@ -0,0 +1,378 @@ +import {expect} from 'chai'; + +import { deepClone, mergeDeep } from 'src/utils'; +import { createEidsArray } from 'modules/userId/eids.js'; + +import {spec as adapter} from 'modules/viouslyBidAdapter'; + +import sinon from 'sinon'; +import { config } from 'src/config.js'; + +const CURRENCY = 'EUR'; +const TTL = 60; +const HTTP_METHOD = 'POST'; +const REQUEST_URL = 'https://bidder.viously.com/bid'; + +const VALID_BID_VIDEO = { + bidder: 'viously', + bidId: '5e6f7g8h', + adUnitCode: 'id-5678', + params: { + pid: '123e4567-e89b-12d3-a456-426614174001' + }, + mediaTypes: { + video: { + playerSize: [640, 360], + context: 'instream', + playbackmethod: [1, 2, 3, 4] + } + } +}; + +const VALID_REQUEST_VIDEO = { + method: HTTP_METHOD, + url: REQUEST_URL, + data: { + pid: '123e4567-e89b-12d3-a456-426614174001', + currency_code: CURRENCY, + placements: [ + { + id: 'id-5678', + bid_id: '5e6f7g8h', + video_params: { + context: 'instream', + playbackmethod: [1, 2, 3, 4], + size: ['640x360'] + } + } + ] + } +}; + +const VALID_GDPR = { + gdprApplies: true, + apiVersion: 2, + consentString: 'abcdefgh', + addtlConsent: '1~12345678', + vendorData: { + purpose: { + consents: { + 1: true + } + } + } +}; +const US_PRIVACY = '1YNN'; + +describe('ViouslyAdapter', function () { + describe('isBidRequestValid', function () { + describe('Check method return', function () { + it('should return true', function () { + expect(adapter.isBidRequestValid(VALID_BID_VIDEO)).to.equal(true); + }); + + it('should return false because the pid is missing', function () { + let wrongBid = deepClone(VALID_BID_VIDEO); + delete wrongBid.params.pid; + + expect(adapter.isBidRequestValid(wrongBid)).to.equal(false); + }); + + it('should return false because the video context parameter is missing', function () { + let wrongBid = deepClone(VALID_BID_VIDEO); + + delete wrongBid.mediaTypes.video.context; + expect(adapter.isBidRequestValid(wrongBid)).to.equal(false); + }); + }); + }); + + describe('buildRequests', function () { + describe('Check method return', function () { + it('should return the right formatted video requests', function() { + expect(adapter.buildRequests([VALID_BID_VIDEO])).to.deep.equal(VALID_REQUEST_VIDEO); + }); + + it('should return the right formatted request with the referer info', function() { + let bidderRequest = { + refererInfo: { + page: 'https://www.example.com/test' + } + }; + + let requests = mergeDeep(deepClone(VALID_REQUEST_VIDEO), { + data: { + domain: 'www.example.com', + page_domain: 'https://www.example.com/test' + } + }); + + expect(adapter.buildRequests([VALID_BID_VIDEO], bidderRequest)).to.deep.equal(requests); + }); + + it('should return the right formatted request with the referer info from config', function() { + /** Mock the config.getConfig method */ + sinon.stub(config, 'getConfig') + .withArgs('pageUrl') + .returns('https://www.example.com/page'); + + let requests = mergeDeep(deepClone(VALID_REQUEST_VIDEO), { + data: { + domain: 'www.example.com', + page_domain: 'https://www.example.com/page' + } + }); + + expect(adapter.buildRequests([VALID_BID_VIDEO])).to.deep.equal(requests); + + config.getConfig.restore(); + }); + + it('should return the right formatted request with GDPR Consent info', function() { + let bidderRequest = { + gdprConsent: VALID_GDPR + }; + + let requests = mergeDeep(deepClone(VALID_REQUEST_VIDEO), { + data: { + gdpr: true, + gdpr_consent: 'abcdefgh', + addtl_consent: '1~12345678' + } + }); + + expect(adapter.buildRequests([VALID_BID_VIDEO], bidderRequest)).to.deep.equal(requests); + }); + + it('should return the right formatted request with US Privacy info', function() { + let bidderRequest = { + uspConsent: US_PRIVACY + }; + + let requests = mergeDeep(deepClone(VALID_REQUEST_VIDEO), { + data: { + us_privacy: US_PRIVACY + } + }); + + expect(adapter.buildRequests([VALID_BID_VIDEO], bidderRequest)).to.deep.equal(requests); + }); + + // TODO: Supply chain + it('should return the right formatted request with Supply Chain info', function() { + let schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'test1.com', + 'sid': '00001', + 'hp': 1 + }, + { + 'asi': 'test2-2.com', + 'sid': '00002', + 'hp': 2 + } + ] + }; + + let bid = mergeDeep(deepClone(VALID_BID_VIDEO), { + schain: schain + }); + + let requests = mergeDeep(deepClone(VALID_REQUEST_VIDEO), { + data: { + schain: schain + } + }); + + expect(adapter.buildRequests([bid])).to.deep.equal(requests); + }); + + it('should return the right formatted request with User Ids info', function() { + let userIds = { + idl_env: '1234-5678-9012-3456', // Liveramp + netId: 'testnetid123', // NetId + IDP: 'userIDP000', // IDP + fabrickId: 'fabrickId9000', // FabrickId + uid2: { id: 'testuid2' } // UID 2.0 + }; + + let bid = mergeDeep(deepClone(VALID_BID_VIDEO), { + userIds: userIds + }, { + userIdAsEids: createEidsArray(userIds) + }); + + let requests = mergeDeep(deepClone(VALID_REQUEST_VIDEO), { + data: { + users_uid: createEidsArray(userIds) + } + }); + + expect(adapter.buildRequests([bid])).to.deep.equal(requests); + }); + + it('should return the right formatted request with endpint test', function() { + let endpoint = 'https://bid-test.viously.com/prebid'; + + let bid = mergeDeep(deepClone(VALID_BID_VIDEO), { + params: { + endpoint: endpoint + } + }); + + let requests = mergeDeep(deepClone(VALID_REQUEST_VIDEO)); + + requests.url = endpoint; + + expect(adapter.buildRequests([bid])).to.deep.equal(requests); + }); + + // TODO: Floor + }); + }); + + describe('interpretResponse', function() { + describe('Check method return', function () { + it('should return the right formatted response', function() { + let response = { + body: { + ads: [ + { + bid: false, + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-0', + bid_id: '1234' + }, + { + bid: true, + creative_id: '2468', + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-1', + bid_id: '5678', + cpm: 8, + ad: 'vast xml', + ad_url: 'http://www.example.com/vast', + type: 'video', + size: '640x480', + nurl: [ + 'win.domain.com' + ] + }, + { + bid: true, + creative_id: '1469', + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-3', + bid_id: '2570', + cpm: 4, + ad: 'vast xml', + type: 'video', + size: '640x480', + } + ] + } + }; + let requests = { + data: { + placements: [ + { + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-0', + bid_id: '1234' + }, + { + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-1', + bid_id: '5678' + }, + { + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-3', + bid_id: '2570' + } + ] + } + }; + + let formattedReponse = [ + { + requestId: '5678', + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-1', + cpm: 8, + width: '640', + height: '480', + creativeId: '2468', + currency: CURRENCY, + netRevenue: true, + ttl: TTL, + mediaType: 'video', + meta: {}, + vastUrl: 'http://www.example.com/vast', + nurl: [ + 'win.domain.com' + ] + }, + { + requestId: '2570', + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-3', + cpm: 4, + width: '640', + height: '480', + creativeId: '1469', + currency: CURRENCY, + netRevenue: true, + ttl: TTL, + mediaType: 'video', + meta: {}, + vastXml: 'vast xml', + nurl: [] + } + ]; + + expect(adapter.interpretResponse(response, requests)).to.deep.equal(formattedReponse); + }); + }); + }); + + describe('onBidWon', function() { + describe('Check methods succeed', function () { + it('should not throw error', function() { + let bids = [ + { + requestId: '5678', + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-1', + cpm: 8, + width: '640', + height: '480', + creativeId: '2468', + currency: CURRENCY, + netRevenue: true, + ttl: TTL, + mediaType: 'video', + meta: {}, + vastUrl: 'http://www.example.com/vast', + nurl: [ + 'win.domain.com' + ] + }, + { + requestId: '2570', + id: 'id-0157324f-bee4-5390-a14c-47a7da3eb73c-3', + cpm: 4, + width: '640', + height: '480', + creativeId: '1469', + currency: CURRENCY, + netRevenue: true, + ttl: TTL, + mediaType: 'video', + meta: {}, + vastXml: 'vast xml', + nurl: [] + } + ]; + + bids.forEach(function(bid) { + expect(adapter.onBidWon.bind(adapter, bid)).to.not.throw(); + }); + }); + }); + }); +}); From 681e2c7fa81fca27f39df81c56f8d162e1815fd2 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 20 Dec 2022 23:09:19 -0700 Subject: [PATCH 537/569] Topics FPD module: fix tests (#9354) --- test/spec/modules/topicsFpdModule_spec.js | 63 +++++++++++++++-------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js index 69e7cc16ca8..958489f2728 100644 --- a/test/spec/modules/topicsFpdModule_spec.js +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -1,4 +1,12 @@ -import {getTopics, getTopicsData, processFpd, hasGDPRConsent, getCachedTopics, receiveMessage, topicStorageName} from '../../../modules/topicsFpdModule.js'; +import { + getTopics, + getTopicsData, + processFpd, + hasGDPRConsent, + getCachedTopics, + receiveMessage, + topicStorageName +} from '../../../modules/topicsFpdModule.js'; import {deepClone, safeJSONParse} from '../../../src/utils.js'; import {gdprDataHandler} from 'src/adapterManager.js'; import {getCoreStorageManager} from 'src/storageManager.js'; @@ -9,14 +17,14 @@ describe('getTopicsData', () => { topic, taxonomyVersion: taxv, modelVersion: modelv - } + }; } function byTaxClass(segments) { return segments.reduce((memo, segment) => { memo[`${segment.ext.segtax}:${segment.ext.segclass}`] = segment; return memo; - }, {}) + }, {}); } [ @@ -146,16 +154,16 @@ describe('getTopicsData', () => { Object.entries(byTaxClass(actual)).forEach(([key, datum]) => { sinon.assert.match(datum, expected[key]); expect(datum.name).to.equal('mockName'); - }) + }); }); it('should not set name if null', () => { getTopicsData(null, topics).forEach((data) => { expect(data.hasOwnProperty('name')).to.be.false; - }) - }) - }) - }) + }); + }); + }); + }); }); describe('getTopics', () => { @@ -169,11 +177,13 @@ describe('getTopics', () => { 'document that throws on featurePolicy': { browsingTopics: sinon.stub(), get featurePolicy() { - throw new Error() + throw new Error(); } }, 'document that throws on browsingTopics': { - browsingTopics: sinon.stub().callsFake(() => { throw new Error(); }), + browsingTopics: sinon.stub().callsFake(() => { + throw new Error(); + }), featurePolicy: { allowsFeature: sinon.stub().returns(true) } @@ -183,11 +193,11 @@ describe('getTopics', () => { return getTopics(doc).then((topics) => { expect(topics).to.eql([]); }); - }) + }); }); it('should call `document.browsingTopics` when allowed', () => { - const topics = ['t1', 't2'] + const topics = ['t1', 't2']; return getTopics({ browsingTopics: sinon.stub().returns(Promise.resolve(topics)), featurePolicy: { @@ -195,9 +205,9 @@ describe('getTopics', () => { } }).then((actual) => { expect(actual).to.eql(topics); - }) - }) -}) + }); + }); +}); describe('processFpd', () => { const mockData = [ @@ -244,7 +254,7 @@ describe('Topics Module GDPR consent check', () => { let gdprDataHdlrStub; beforeEach(() => { gdprDataHdlrStub = sinon.stub(gdprDataHandler, 'getConsentData'); - }) + }); afterEach(() => { gdprDataHdlrStub.restore(); @@ -261,7 +271,7 @@ describe('Topics Module GDPR consent check', () => { expect(hasGDPRConsent()).to.equal(false); }); - it("should return true when GDPR doesn't apply", () => { + it('should return true when GDPR doesn\'t apply', () => { const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; const consentConfig = { consentString: consentString, @@ -365,7 +375,7 @@ describe('getCachedTopics()', () => { const evt = { data: '{"segment":{"domain":"ads.pubmatic.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"pubmatic"},"date":1669743901858}', origin: 'https://ads.pubmatic.com' - } + }; let gdprDataHdlrStub; beforeEach(() => { @@ -378,14 +388,23 @@ describe('getCachedTopics()', () => { }); it('should return segments for bidder if GDPR consent is true and there is cached segments stored which is not expired', () => { - let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1669719242027}]]'; + const storedSegments = JSON.stringify( + [['pubmatic', { + '2206021246': { + 'ext': {'segtax': 600, 'segclass': '2206021246'}, + 'segment': [{'id': '243'}, {'id': '265'}], + 'name': 'ads.pubmatic.com' + }, + 'lastUpdated': new Date().getTime() + }]] + ); storage.setDataInLocalStorage(topicStorageName, storedSegments); gdprDataHdlrStub.returns(consentConfig); assert.deepEqual(getCachedTopics(), expected); }); it('should return empty segments for bidder if GDPR consent is true and there is cached segments stored which is expired', () => { - let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1659719242027}]]'; + let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":10}]]'; storage.setDataInLocalStorage(topicStorageName, storedSegments); gdprDataHdlrStub.returns(consentConfig); assert.deepEqual(getCachedTopics(), []); @@ -394,7 +413,7 @@ describe('getCachedTopics()', () => { it('should stored segments if receiveMessage event is triggerred with segment data', () => { return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) .then(({global}) => { - receiveMessage(evt) + receiveMessage(evt); let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); expect(segments.has('pubmatic')).to.equal(true); }); @@ -410,4 +429,4 @@ describe('getCachedTopics()', () => { expect(segments.get('pubmatic')[2206021246].segment.length).to.equal(1); }); }); -}) +}); From 77647180e5fc3c8058adcf5d642499ad3146b495 Mon Sep 17 00:00:00 2001 From: mjaworskiccx <50406214+mjaworskiccx@users.noreply.github.com> Date: Wed, 21 Dec 2022 16:39:56 +0100 Subject: [PATCH 538/569] Ccx Bid Adapter: Add GVLID param (#9359) * adomain support * adomain support * adomain support * adomain support * adomain support * video params * docs changes * Clickonometrics adapter update * Revert "Revert "Clickonometrics Bid Adapter : add gvlid (#9198)" (#9216)" This reverts commit 6d114e83725b403fadd889202b449de225db7275. --- modules/ccxBidAdapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 7c6b0411023..efdb8992669 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -5,6 +5,7 @@ import {getStorageManager} from '../src/storageManager.js'; const BIDDER_CODE = 'ccx' const storage = getStorageManager({bidderCode: BIDDER_CODE}); const BID_URL = 'https://delivery.clickonometrics.pl/ortb/prebid/bid' +const GVLID = 773; const SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6] const SUPPORTED_VIDEO_MIMES = ['video/mp4', 'video/x-flv'] const SUPPORTED_VIDEO_PLAYBACK_METHODS = [1, 2, 3, 4] @@ -19,8 +20,7 @@ function _getDeviceObj () { function _getSiteObj (bidderRequest) { let site = {} - // TODO: does the fallback to window.location make sense? - let url = bidderRequest?.refererInfo?.page || window.location.href + let url = bidderRequest?.refererInfo?.page if (url.length > 0) { url = url.split('?')[0] } @@ -140,6 +140,7 @@ function _buildResponse (bid, currency, ttl) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { From 51c984e7088a7bdf13da4c36c988938711f0089a Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 21 Dec 2022 11:52:06 -0500 Subject: [PATCH 539/569] Revert "Ccx Bid Adapter: Add GVLID param (#9359)" (#9363) This reverts commit 77647180e5fc3c8058adcf5d642499ad3146b495. Co-authored-by: Demetrio Girardi --- modules/ccxBidAdapter.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index efdb8992669..7c6b0411023 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -5,7 +5,6 @@ import {getStorageManager} from '../src/storageManager.js'; const BIDDER_CODE = 'ccx' const storage = getStorageManager({bidderCode: BIDDER_CODE}); const BID_URL = 'https://delivery.clickonometrics.pl/ortb/prebid/bid' -const GVLID = 773; const SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6] const SUPPORTED_VIDEO_MIMES = ['video/mp4', 'video/x-flv'] const SUPPORTED_VIDEO_PLAYBACK_METHODS = [1, 2, 3, 4] @@ -20,7 +19,8 @@ function _getDeviceObj () { function _getSiteObj (bidderRequest) { let site = {} - let url = bidderRequest?.refererInfo?.page + // TODO: does the fallback to window.location make sense? + let url = bidderRequest?.refererInfo?.page || window.location.href if (url.length > 0) { url = url.split('?')[0] } @@ -140,7 +140,6 @@ function _buildResponse (bid, currency, ttl) { export const spec = { code: BIDDER_CODE, - gvlid: GVLID, supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { From e2cfc185345b5f552d18d50cba8c880b4a9ecae7 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Wed, 21 Dec 2022 12:33:22 -0500 Subject: [PATCH 540/569] GPP consent module: phase one release (#9321) * GPP consent module phase 1 * various updates and added test pages * revise calling CMP, remove provisionalConsent, remove cmpDisplayStatus check, update pbs usersync * change callback check to be more strict * update logic on adding gpp data to ortb2 * update gpp metadata --- .../gpt/gpp_us_hello_world.html | 124 ++++ .../gpt/gpp_us_hello_world_iframe.html | 12 + .../gpp_us_hello_world_iframe_subpage.html | 140 +++++ modules/appnexusBidAdapter.js | 25 +- modules/bidViewability.js | 7 +- modules/consentManagement.js | 8 +- modules/consentManagementGpp.js | 386 ++++++++++++ modules/dfpAdServerVideo.js | 7 +- modules/prebidServerBidAdapter/index.js | 27 +- modules/rtdModule/index.js | 3 +- src/adapterManager.js | 21 +- src/adapters/bidderFactory.js | 10 +- src/consentHandler.js | 11 + src/prebid.js | 3 +- test/spec/modules/appnexusBidAdapter_spec.js | 46 ++ .../spec/modules/consentManagementGpp_spec.js | 577 ++++++++++++++++++ 16 files changed, 1374 insertions(+), 33 deletions(-) create mode 100644 integrationExamples/gpt/gpp_us_hello_world.html create mode 100644 integrationExamples/gpt/gpp_us_hello_world_iframe.html create mode 100644 integrationExamples/gpt/gpp_us_hello_world_iframe_subpage.html create mode 100644 modules/consentManagementGpp.js create mode 100644 test/spec/modules/consentManagementGpp_spec.js diff --git a/integrationExamples/gpt/gpp_us_hello_world.html b/integrationExamples/gpt/gpp_us_hello_world.html new file mode 100644 index 00000000000..28be86127fc --- /dev/null +++ b/integrationExamples/gpt/gpp_us_hello_world.html @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+ +
+ +
+ + + \ No newline at end of file diff --git a/integrationExamples/gpt/gpp_us_hello_world_iframe.html b/integrationExamples/gpt/gpp_us_hello_world_iframe.html new file mode 100644 index 00000000000..c0a62f9d72e --- /dev/null +++ b/integrationExamples/gpt/gpp_us_hello_world_iframe.html @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/integrationExamples/gpt/gpp_us_hello_world_iframe_subpage.html b/integrationExamples/gpt/gpp_us_hello_world_iframe_subpage.html new file mode 100644 index 00000000000..8c2096d614d --- /dev/null +++ b/integrationExamples/gpt/gpp_us_hello_world_iframe_subpage.html @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+ +
+ +
+ + + \ No newline at end of file diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 77ffe0f6b94..919831b8515 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -304,6 +304,18 @@ export const spec = { payload.us_privacy = bidderRequest.uspConsent; } + if (bidderRequest?.gppConsent) { + payload.privacy = { + gpp: bidderRequest.gppConsent.gppString, + gpp_sid: bidderRequest.gppConsent.applicableSections + } + } else if (bidderRequest?.ortb2?.regs?.gpp) { + payload.privacy = { + gpp: bidderRequest.ortb2.regs.gpp, + gpp_sid: bidderRequest.ortb2.regs.gpp_sid + } + } + if (bidderRequest && bidderRequest.refererInfo) { let refererinfo = { // TODO: are these the correct referer values? @@ -424,8 +436,17 @@ export const spec = { } }, - getUserSyncs: function (syncOptions, responses, gdprConsent) { - if (syncOptions.iframeEnabled && hasPurpose1Consent(gdprConsent)) { + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { + function checkGppStatus(gppConsent) { + // this is a temporary measure to supress usersync in US-based GPP regions + // this logic will be revised when proper signals (akin to purpose1 from TCF2) can be determined for US GPP + if (gppConsent && Array.isArray(gppConsent.applicableSections)) { + return gppConsent.applicableSections.every(sec => typeof sec === 'number' && sec <= 5); + } + return true; + } + + if (syncOptions.iframeEnabled && hasPurpose1Consent(gdprConsent) && checkGppStatus(gppConsent)) { return [{ type: 'iframe', url: 'https://acdn.adnxs.com/dmp/async_usersync.html' diff --git a/modules/bidViewability.js b/modules/bidViewability.js index 362401e6d1c..a5cab99b1a7 100644 --- a/modules/bidViewability.js +++ b/modules/bidViewability.js @@ -7,7 +7,7 @@ import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {isFn, logWarn, triggerPixel} from '../src/utils.js'; import {getGlobal} from '../src/prebidGlobal.js'; -import adapterManager, {gdprDataHandler, uspDataHandler} from '../src/adapterManager.js'; +import adapterManager, {gdprDataHandler, uspDataHandler, gppDataHandler} from '../src/adapterManager.js'; import {find} from '../src/polyfill.js'; const MODULE_NAME = 'bidViewability'; @@ -44,6 +44,11 @@ export let fireViewabilityPixels = (globalModuleConfig, bid) => { const uspConsent = uspDataHandler.getConsentData(); if (uspConsent) { queryParams.us_privacy = uspConsent; } + const gppConsent = gppDataHandler.getConsentData(); + if (gppConsent) { + // TODO - need to know what to set here for queryParams... + } + bid[BID_VURL_ARRAY].forEach(url => { // add '?' if not present in URL if (Object.keys(queryParams).length > 0 && url.indexOf('?') === -1) { diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 6ca12010c74..217ceecb1c4 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -94,7 +94,7 @@ function lookupIabConsent({onSuccess, onError}) { const { cmpFrame, cmpFunction } = findCMP(); if (!cmpFrame) { - return onError('CMP not found.'); + return onError('TCF2 CMP not found.'); } // to collect the consent information from the user, we perform two calls to the CMP in parallel: // first to collect the user's consent choices represented in an encoded string (via getConsentData) @@ -314,11 +314,11 @@ export function resetConsentData() { * @param {{cmp:string, timeout:number, allowAuctionWithoutConsent:boolean, defaultGdprScope:boolean}} config required; consentManagement module config settings; cmp (string), timeout (int), allowAuctionWithoutConsent (boolean) */ export function setConsentConfig(config) { - // if `config.gdpr` or `config.usp` exist, assume new config format. + // if `config.gdpr`, `config.usp` or `config.gpp` exist, assume new config format. // else for backward compatability, just use `config` - config = config && (config.gdpr || config.usp ? config.gdpr : config); + config = config && (config.gdpr || config.usp || config.gpp ? config.gdpr : config); if (!config || typeof config !== 'object') { - logWarn('consentManagement config not defined, exiting consent manager'); + logWarn('consentManagement (gdpr) config not defined, exiting consent manager'); return; } if (isStr(config.cmpApi)) { diff --git a/modules/consentManagementGpp.js b/modules/consentManagementGpp.js new file mode 100644 index 00000000000..8a9c3f999f0 --- /dev/null +++ b/modules/consentManagementGpp.js @@ -0,0 +1,386 @@ +/** + * This module adds GPP consentManagement support to prebid.js. It interacts with + * supported CMPs (Consent Management Platforms) to grab the user's consent information + * and make it available for any GPP supported adapters to read/pass this information to + * their system and for various other features/modules in Prebid.js. + */ +import {deepSetValue, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js'; +import {config} from '../src/config.js'; +import {gppDataHandler} from '../src/adapterManager.js'; +import {includes} from '../src/polyfill.js'; +import {timedAuctionHook} from '../src/utils/perfMetrics.js'; +import { enrichFPD } from '../src/fpd/enrichment.js'; + +const DEFAULT_CMP = 'iab'; +const DEFAULT_CONSENT_TIMEOUT = 10000; +const CMP_VERSION = 1; + +export let userCMP; +export let consentTimeout; +export let staticConsentData; + +let consentData; +let addedConsentHook = false; + +// add new CMPs here, with their dedicated lookup function +const cmpCallMap = { + 'iab': lookupIabConsent, + 'static': lookupStaticConsentData +}; + +/** + * This function checks the state of the IAB gppData's applicableSection field (to ensure it's populated and has a valid value). + * section === 0 represents a CMP's default value when CMP is loading, it shoud not be used a real user's section. + * + * TODO --- The initial version of the GPP CMP API spec used this naming convention, but it was later changed as an update to the spec. + * CMPs should adjust their logic to use the new format (applicableSecctions), but that may not be the case with the initial release. + * Added support just in case for this transition period, can likely be removed at a later date... + * @param gppData represents the IAB gppData object + * @returns true|false + */ +function checkApplicableSectionIsReady(gppData) { + return gppData && Array.isArray(gppData.applicableSection) && gppData.applicableSection.length > 0 && gppData.applicableSection[0] !== 0; +} + +/** + * This function checks the state of the IAB gppData's applicableSections field (to ensure it's populated and has a valid value). + * section === 0 represents a CMP's default value when CMP is loading, it shoud not be used a real user's section. + * @param gppData represents the IAB gppData object + * @returns true|false + */ +function checkApplicableSectionsIsReady(gppData) { + return gppData && Array.isArray(gppData.applicableSections) && gppData.applicableSections.length > 0 && gppData.applicableSections[0] !== 0; +} + +/** + * This function reads the consent string from the config to obtain the consent information of the user. + * @param {function({})} onSuccess acts as a success callback when the value is read from config; pass along consentObject from CMP + */ +function lookupStaticConsentData({onSuccess, onError}) { + processCmpData(staticConsentData, {onSuccess, onError}); +} + +/** + * This function handles interacting with an IAB compliant CMP to obtain the consent information of the user. + * Given the async nature of the CMP's API, we pass in acting success/error callback functions to exit this function + * based on the appropriate result. + * @param {function({})} onSuccess acts as a success callback when CMP returns a value; pass along consentObjectfrom CMP + * @param {function(string, ...{}?)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string) and any extra error arguments (purely for logging) + */ +function lookupIabConsent({onSuccess, onError}) { + const cmpApiName = '__gpp'; + const cmpCallbacks = {}; + let registeredPostMessageResponseListener = false; + + function findCMP() { + let f = window; + let cmpFrame; + let cmpDirectAccess = false; + while (true) { + try { + if (typeof f[cmpApiName] === 'function') { + cmpFrame = f; + cmpDirectAccess = true; + break; + } + } catch (e) {} + + // need separate try/catch blocks due to the exception errors thrown when trying to check for a frame that doesn't exist in 3rd party env + try { + if (f.frames['__gppLocator']) { + cmpFrame = f; + break; + } + } catch (e) {} + + if (f === window.top) break; + f = f.parent; + } + + return { + cmpFrame, + cmpDirectAccess + }; + } + + const {cmpFrame, cmpDirectAccess} = findCMP(); + + if (!cmpFrame) { + return onError('GPP CMP not found.'); + } + + const invokeCMP = (cmpDirectAccess) ? invokeCMPDirect : invokeCMPFrame; + + function invokeCMPDirect({command, callback, parameter, version = CMP_VERSION}, resultCb) { + if (typeof resultCb === 'function') { + resultCb(cmpFrame[cmpApiName](command, callback, parameter, version)); + } else { + cmpFrame[cmpApiName](command, callback, parameter, version); + } + } + + function invokeCMPFrame({command, callback, parameter, version = CMP_VERSION}, resultCb) { + const callName = `${cmpApiName}Call`; + if (!registeredPostMessageResponseListener) { + // when we get the return message, call the stashed callback; + window.addEventListener('message', readPostMessageResponse, false); + registeredPostMessageResponseListener = true; + } + + // call CMP via postMessage + const callId = Math.random().toString(); + const msg = { + [callName]: { + command: command, + parameter, + version, + callId: callId + } + }; + + // TODO? - add logic to check if random was already used in the same session, and roll another if so? + cmpCallbacks[callId] = (typeof callback === 'function') ? callback : resultCb; + cmpFrame.postMessage(msg, '*'); + + function readPostMessageResponse(event) { + const cmpDataPkgName = `${cmpApiName}Return`; + const json = (typeof event.data === 'string' && event.data.includes(cmpDataPkgName)) ? JSON.parse(event.data) : event.data; + if (json[cmpDataPkgName] && json[cmpDataPkgName].callId) { + const payload = json[cmpDataPkgName]; + + if (cmpCallbacks.hasOwnProperty(payload.callId)) { + cmpCallbacks[payload.callId](payload.returnValue); + } + } + } + } + + const startupMsg = (cmpDirectAccess) ? 'Detected GPP CMP API is directly accessible, calling it now...' + : 'Detected GPP CMP is outside the current iframe where Prebid.js is located, calling it now...'; + logInfo(startupMsg); + + invokeCMP({ + command: 'addEventListener', + callback: function (evt) { + if (evt) { + logInfo(`Received a ${(cmpDirectAccess ? 'direct' : 'postmsg')} response from GPP CMP for event`, evt); + if (evt.eventName === 'sectionChange' || evt.pingData.cmpStatus === 'loaded') { + invokeCMP({command: 'getGPPData'}, function (gppData) { + logInfo(`Received a ${cmpDirectAccess ? 'direct' : 'postmsg'} response from GPP CMP for getGPPData`, gppData); + processCmpData(gppData, {onSuccess, onError}); + }); + } else if (evt.pingData.cmpStatus === 'error') { + onError('CMP returned with a cmpStatus:error response. Please check CMP setup.'); + } + } + } + }); +} + +/** + * Look up consent data and store it in the `consentData` global as well as `adapterManager.js`' gdprDataHandler. + * + * @param cb A callback that takes: a boolean that is true if the auction should be canceled; an error message and extra + * error arguments that will be undefined if there's no error. + */ +function loadConsentData(cb) { + let isDone = false; + let timer = null; + + function done(consentData, shouldCancelAuction, errMsg, ...extraArgs) { + if (timer != null) { + clearTimeout(timer); + } + isDone = true; + gppDataHandler.setConsentData(consentData); + if (typeof cb === 'function') { + cb(shouldCancelAuction, errMsg, ...extraArgs); + } + } + + if (!includes(Object.keys(cmpCallMap), userCMP)) { + done(null, false, `GPP CMP framework (${userCMP}) is not a supported framework. Aborting consentManagement module and resuming auction.`); + return; + } + + const callbacks = { + onSuccess: (data) => done(data, false), + onError: function (msg, ...extraArgs) { + done(null, true, msg, ...extraArgs); + } + } + cmpCallMap[userCMP](callbacks); + + if (!isDone) { + const onTimeout = () => { + const continueToAuction = (data) => { + done(data, false, 'GPP CMP did not load, continuing auction...'); + } + processCmpData(consentData, { + onSuccess: continueToAuction, + onError: () => continueToAuction(storeConsentData(undefined)) + }) + } + if (consentTimeout === 0) { + onTimeout(); + } else { + timer = setTimeout(onTimeout, consentTimeout); + } + } +} + +/** + * Like `loadConsentData`, but cache and re-use previously loaded data. + * @param cb + */ +function loadIfMissing(cb) { + if (consentData) { + logInfo('User consent information already known. Pulling internally stored information...'); + // eslint-disable-next-line standard/no-callback-literal + cb(false); + } else { + loadConsentData(cb); + } +} + +/** + * If consentManagement module is enabled (ie included in setConfig), this hook function will attempt to fetch the + * user's encoded consent string from the supported CMP. Once obtained, the module will store this + * data as part of a gppConsent object which gets transferred to adapterManager's gppDataHandler object. + * This information is later added into the bidRequest object for any supported adapters to read/pass along to their system. + * @param {object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids. + * @param {function} fn required; The next function in the chain, used by hook.js + */ +export const requestBidsHook = timedAuctionHook('gpp', function requestBidsHook(fn, reqBidsConfigObj) { + loadIfMissing(function (shouldCancelAuction, errMsg, ...extraArgs) { + if (errMsg) { + let log = logWarn; + if (shouldCancelAuction) { + log = logError; + errMsg = `${errMsg} Canceling auction as per consentManagement config.`; + } + log(errMsg, ...extraArgs); + } + + if (shouldCancelAuction) { + fn.stopTiming(); + if (typeof reqBidsConfigObj.bidsBackHandler === 'function') { + reqBidsConfigObj.bidsBackHandler(); + } else { + logError('Error executing bidsBackHandler'); + } + } else { + fn.call(this, reqBidsConfigObj); + } + }); +}); + +/** + * This function checks the consent data provided by CMP to ensure it's in an expected state. + * If it's bad, we call `onError` + * If it's good, then we store the value and call `onSuccess` + */ +function processCmpData(consentObject, {onSuccess, onError}) { + function checkData() { + const gppString = consentObject && consentObject.gppString; + const gppSection = (checkApplicableSectionsIsReady(consentObject)) ? consentObject.applicableSections + : (checkApplicableSectionIsReady(consentObject)) ? consentObject.applicableSection : []; + + return !!( + (!Array.isArray(gppSection)) || + (Array.isArray(gppSection) && (!gppString || !isStr(gppString))) + ); + } + + if (checkData()) { + onError(`CMP returned unexpected value during lookup process.`, consentObject); + } else { + onSuccess(storeConsentData(consentObject)); + } +} + +/** + * Stores CMP data locally in module to make information available in adaptermanager.js for later in the auction + * @param {object} cmpConsentObject required; an object representing user's consent choices (can be undefined in certain use-cases for this function only) + */ +function storeConsentData(cmpConsentObject) { + consentData = { + gppString: (cmpConsentObject) ? cmpConsentObject.gppString : undefined, + + fullGppData: (cmpConsentObject) || undefined, + }; + consentData.applicableSections = (checkApplicableSectionsIsReady(cmpConsentObject)) ? cmpConsentObject.applicableSections + : (checkApplicableSectionIsReady(cmpConsentObject)) ? cmpConsentObject.applicableSection : []; + consentData.apiVersion = CMP_VERSION; + return consentData; +} + +/** + * Simply resets the module's consentData variable back to undefined, mainly for testing purposes + */ +export function resetConsentData() { + consentData = undefined; + userCMP = undefined; + consentTimeout = undefined; + gppDataHandler.reset(); +} + +/** + * A configuration function that initializes some module variables, as well as add a hook into the requestBids function + * @param {{cmp:string, timeout:number, allowAuctionWithoutConsent:boolean, defaultGdprScope:boolean}} config required; consentManagement module config settings; cmp (string), timeout (int), allowAuctionWithoutConsent (boolean) + */ +export function setConsentConfig(config) { + config = config && config.gpp; + if (!config || typeof config !== 'object') { + logWarn('consentManagement.gpp config not defined, exiting consent manager module'); + return; + } + + if (isStr(config.cmpApi)) { + userCMP = config.cmpApi; + } else { + userCMP = DEFAULT_CMP; + logInfo(`consentManagement.gpp config did not specify cmp. Using system default setting (${DEFAULT_CMP}).`); + } + + if (isNumber(config.timeout)) { + consentTimeout = config.timeout; + } else { + consentTimeout = DEFAULT_CONSENT_TIMEOUT; + logInfo(`consentManagement.gpp config did not specify timeout. Using system default setting (${DEFAULT_CONSENT_TIMEOUT}).`); + } + + if (userCMP === 'static') { + if (isPlainObject(config.consentData)) { + staticConsentData = config.consentData; + consentTimeout = 0; + } else { + logError(`consentManagement.gpp config with cmpApi: 'static' did not specify consentData. No consents will be available to adapters.`); + } + } + + logInfo('consentManagement.gpp module has been activated...'); + + if (!addedConsentHook) { + $$PREBID_GLOBAL$$.requestBids.before(requestBidsHook, 50); + } + addedConsentHook = true; + gppDataHandler.enable(); + loadConsentData(); // immediately look up consent data to make it available without requiring an auction +} +config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement)); + +export function enrichFPDHook(next, fpd) { + return next(fpd.then(ortb2 => { + const consent = gppDataHandler.getConsentData(); + if (consent) { + if (Array.isArray(consent.applicableSections)) { + deepSetValue(ortb2, 'regs.gpp_sid', consent.applicableSections); + } + deepSetValue(ortb2, 'regs.gpp', consent.gppString); + } + return ortb2; + })); +} + +enrichFPD.before(enrichFPDHook); diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 072715b4bd6..71c9c7aa341 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -8,7 +8,7 @@ import { deepAccess, isEmpty, logError, parseSizesInput, formatQS, parseUrl, bui import { config } from '../src/config.js'; import { getHook, submodule } from '../src/hook.js'; import { auctionManager } from '../src/auctionManager.js'; -import { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js'; +import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {getPPID} from '../src/adserver.js'; @@ -119,6 +119,11 @@ export function buildDfpVideoUrl(options) { const uspConsent = uspDataHandler.getConsentData(); if (uspConsent) { queryParams.us_privacy = uspConsent; } + const gppConsent = gppDataHandler.getConsentData(); + if (gppConsent) { + // TODO - need to know what to set here for queryParams... + } + if (!queryParams.ppid) { const ppid = getPPID(); if (ppid != null) { diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 072c280aecf..b609d1a54ec 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -216,7 +216,7 @@ export function resetSyncedStatus() { /** * @param {Array} bidderCodes list of bidders to request user syncs for. */ -function queueSync(bidderCodes, gdprConsent, uspConsent, s2sConfig) { +function queueSync(bidderCodes, gdprConsent, uspConsent, gppConsent, s2sConfig) { if (_s2sConfigs.length === _syncCount) { return; } @@ -246,6 +246,15 @@ function queueSync(bidderCodes, gdprConsent, uspConsent, s2sConfig) { payload.us_privacy = uspConsent; } + if (gppConsent) { + // proposing the following formatting, can adjust if needed... + // update - leaving this param as an array, since it's part of a POST payload where the [] characters shouldn't matter too much + payload.gpp_sid = gppConsent.applicableSections + // should we add check if applicableSections was not equal to -1 (where user was out of scope)? + // this would be similar to what was done above for TCF + payload.gpp = gppConsent.gppString; + } + if (typeof s2sConfig.coopSync === 'boolean') { payload.coopSync = s2sConfig.coopSync; } @@ -330,7 +339,7 @@ function doBidderSync(type, url, bidder, done, timeout) { * * @param {Array} bidders a list of bidder names */ -function doClientSideSyncs(bidders, gdprConsent, uspConsent) { +function doClientSideSyncs(bidders, gdprConsent, uspConsent, gppConsent) { bidders.forEach(bidder => { let clientAdapter = adapterManager.getBidAdapter(bidder); if (clientAdapter && clientAdapter.registerSyncs) { @@ -341,7 +350,8 @@ function doClientSideSyncs(bidders, gdprConsent, uspConsent) { clientAdapter, [], gdprConsent, - uspConsent + uspConsent, + gppConsent ) ); } @@ -411,12 +421,13 @@ function getMatchingConsentUrl(urlProp, gdprConsent) { } function getConsentData(bidRequests) { - let gdprConsent, uspConsent; + let gdprConsent, uspConsent, gppConsent; if (Array.isArray(bidRequests) && bidRequests.length > 0) { gdprConsent = bidRequests[0].gdprConsent; uspConsent = bidRequests[0].uspConsent; + gppConsent = bidRequests[0].gppConsent; } - return { gdprConsent, uspConsent }; + return { gdprConsent, uspConsent, gppConsent }; } /** @@ -433,7 +444,7 @@ export function PrebidServer() { done = adapterMetrics.startTiming('total').stopBefore(done); bidRequests.forEach(req => useMetrics(req.metrics).join(adapterMetrics, {continuePropagation: false})); - let { gdprConsent, uspConsent } = getConsentData(bidRequests); + let { gdprConsent, uspConsent, gppConsent } = getConsentData(bidRequests); if (Array.isArray(_s2sConfigs)) { if (s2sBidRequest.s2sConfig && s2sBidRequest.s2sConfig.syncEndpoint && getMatchingConsentUrl(s2sBidRequest.s2sConfig.syncEndpoint, gdprConsent)) { @@ -441,7 +452,7 @@ export function PrebidServer() { .map(bidder => adapterManager.aliasRegistry[bidder] || bidder) .filter((bidder, index, array) => (array.indexOf(bidder) === index)); - queueSync(syncBidders, gdprConsent, uspConsent, s2sBidRequest.s2sConfig); + queueSync(syncBidders, gdprConsent, uspConsent, gppConsent, s2sBidRequest.s2sConfig); } processPBSRequest(s2sBidRequest, bidRequests, ajax, { @@ -450,7 +461,7 @@ export function PrebidServer() { bidRequests.forEach(bidderRequest => events.emit(CONSTANTS.EVENTS.BIDDER_DONE, bidderRequest)); } done(); - doClientSideSyncs(requestedBidders, gdprConsent, uspConsent); + doClientSideSyncs(requestedBidders, gdprConsent, uspConsent, gppConsent); }, onError: done, onBid: function ({adUnit, bid}) { diff --git a/modules/rtdModule/index.js b/modules/rtdModule/index.js index 876da8dda37..05594c63132 100644 --- a/modules/rtdModule/index.js +++ b/modules/rtdModule/index.js @@ -163,7 +163,7 @@ import {getHook, module} from '../../src/hook.js'; import {logError, logInfo, logWarn} from '../../src/utils.js'; import * as events from '../../src/events.js'; import CONSTANTS from '../../src/constants.json'; -import adapterManager, {gdprDataHandler, uspDataHandler} from '../../src/adapterManager.js'; +import adapterManager, {gdprDataHandler, uspDataHandler, gppDataHandler} from '../../src/adapterManager.js'; import {find} from '../../src/polyfill.js'; import {timedAuctionHook} from '../../src/utils/perfMetrics.js'; @@ -246,6 +246,7 @@ function getConsentData() { return { gdpr: gdprDataHandler.getConsentData(), usp: uspDataHandler.getConsentData(), + gpp: gppDataHandler.getConsentData(), coppa: !!(config.getConfig('coppa')) } } diff --git a/src/adapterManager.js b/src/adapterManager.js index eb6a74a82a4..f4f9e59fb84 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -31,7 +31,7 @@ import {hook} from './hook.js'; import {find, includes} from './polyfill.js'; import {adunitCounter} from './adUnits.js'; import {getRefererInfo} from './refererDetection.js'; -import {GdprConsentHandler, UspConsentHandler} from './consentHandler.js'; +import {GdprConsentHandler, UspConsentHandler, GppConsentHandler} from './consentHandler.js'; import * as events from './events.js'; import CONSTANTS from './constants.json'; import {useMetrics} from './utils/perfMetrics.js'; @@ -161,6 +161,7 @@ function getAdUnitCopyForClientAdapters(adUnits) { export let gdprDataHandler = new GdprConsentHandler(); export let uspDataHandler = new UspConsentHandler(); +export let gppDataHandler = new GppConsentHandler(); export let coppaDataHandler = { getCoppa: function() { @@ -309,17 +310,17 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a } }); - if (gdprDataHandler.getConsentData()) { - bidRequests.forEach(bidRequest => { + bidRequests.forEach(bidRequest => { + if (gdprDataHandler.getConsentData()) { bidRequest['gdprConsent'] = gdprDataHandler.getConsentData(); - }); - } - - if (uspDataHandler.getConsentData()) { - bidRequests.forEach(bidRequest => { + } + if (uspDataHandler.getConsentData()) { bidRequest['uspConsent'] = uspDataHandler.getConsentData(); - }); - } + } + if (gppDataHandler.getConsentData()) { + bidRequest['gppConsent'] = gppDataHandler.getConsentData(); + } + }); bidRequests.forEach(bidRequest => { config.runWithBidder(bidRequest.bidderCode, () => { diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 7e66b849783..55c84e57062 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -214,7 +214,7 @@ export function newBidder(spec) { done(); config.runWithBidder(spec.code, () => { events.emit(CONSTANTS.EVENTS.BIDDER_DONE, bidderRequest); - registerSyncs(responses, bidderRequest.gdprConsent, bidderRequest.uspConsent); + registerSyncs(responses, bidderRequest.gdprConsent, bidderRequest.uspConsent, bidderRequest.gppConsent); }); } @@ -295,8 +295,8 @@ export function newBidder(spec) { return false; } - function registerSyncs(responses, gdprConsent, uspConsent) { - registerSyncInner(spec, responses, gdprConsent, uspConsent); + function registerSyncs(responses, gdprConsent, uspConsent, gppConsent) { + registerSyncInner(spec, responses, gdprConsent, uspConsent, gppConsent); } function filterAndWarn(bid) { @@ -448,14 +448,14 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe }) }, 'processBidderRequests') -export const registerSyncInner = hook('async', function(spec, responses, gdprConsent, uspConsent) { +export const registerSyncInner = hook('async', function(spec, responses, gdprConsent, uspConsent, gppConsent) { const aliasSyncEnabled = config.getConfig('userSync.aliasSyncEnabled'); if (spec.getUserSyncs && (aliasSyncEnabled || !adapterManager.aliasRegistry[spec.code])) { let filterConfig = config.getConfig('userSync.filterSettings'); let syncs = spec.getUserSyncs({ iframeEnabled: !!(filterConfig && (filterConfig.iframe || filterConfig.all)), pixelEnabled: !!(filterConfig && (filterConfig.image || filterConfig.all)), - }, responses, gdprConsent, uspConsent); + }, responses, gdprConsent, uspConsent, gppConsent); if (syncs) { if (!Array.isArray(syncs)) { syncs = [syncs]; diff --git a/src/consentHandler.js b/src/consentHandler.js index 01470a4b38c..b1b2a04c043 100644 --- a/src/consentHandler.js +++ b/src/consentHandler.js @@ -107,3 +107,14 @@ export class GdprConsentHandler extends ConsentHandler { } } } + +export class GppConsentHandler extends ConsentHandler { + getConsentMeta() { + const consentData = this.getConsentData(); + if (consentData && this.generatedTime) { + return { + generatedAt: this.generatedTime, + } + } + } +} diff --git a/src/prebid.js b/src/prebid.js index f30b4ea9bbe..a4abceb01d5 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -45,7 +45,7 @@ import {executeRenderer, isRendererRequired} from './Renderer.js'; import {createBid} from './bidfactory.js'; import {storageCallbacks} from './storageManager.js'; import {emitAdRenderFail, emitAdRenderSucceeded} from './adRendering.js'; -import {default as adapterManager, gdprDataHandler, getS2SBidderSet, uspDataHandler} from './adapterManager.js'; +import {default as adapterManager, gdprDataHandler, getS2SBidderSet, gppDataHandler, uspDataHandler} from './adapterManager.js'; import CONSTANTS from './constants.json'; import * as events from './events.js'; import {newMetrics, useMetrics} from './utils/perfMetrics.js'; @@ -336,6 +336,7 @@ function getConsentMetadata() { return { gdpr: gdprDataHandler.getConsentMeta(), usp: uspDataHandler.getConsentMeta(), + gpp: gppDataHandler.getConsentMeta(), coppa: !!(config.getConfig('coppa')) } } diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 5cd189da9a1..1ab8feceaeb 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -985,6 +985,52 @@ describe('AppNexusAdapter', function () { expect(payload.us_privacy).to.exist.and.to.equal(consentString); }); + it('should add gpp information to the request via bidderRequest.gppConsent', function () { + let consentString = 'abc1234'; + let bidderRequest = { + 'bidderCode': 'appnexus', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gppConsent': { + 'gppString': consentString, + 'applicableSections': [8] + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.privacy).to.exist; + expect(payload.privacy.gpp).to.equal(consentString); + expect(payload.privacy.gpp_sid).to.deep.equal([8]); + }); + + it('should add gpp information to the request via bidderRequest.ortb2.regs', function () { + let consentString = 'abc1234'; + let bidderRequest = { + 'bidderCode': 'appnexus', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'ortb2': { + 'regs': { + 'gpp': consentString, + 'gpp_sid': [7] + } + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.privacy).to.exist; + expect(payload.privacy.gpp).to.equal(consentString); + expect(payload.privacy.gpp_sid).to.deep.equal([7]); + }); + it('supports sending hybrid mobile app parameters', function () { let appRequest = Object.assign({}, bidRequests[0], diff --git a/test/spec/modules/consentManagementGpp_spec.js b/test/spec/modules/consentManagementGpp_spec.js new file mode 100644 index 00000000000..1170f418caf --- /dev/null +++ b/test/spec/modules/consentManagementGpp_spec.js @@ -0,0 +1,577 @@ +import { setConsentConfig, requestBidsHook, resetConsentData, userCMP, consentTimeout, staticConsentData } from 'modules/consentManagementGpp.js'; +import { gppDataHandler } from 'src/adapterManager.js'; +import * as utils from 'src/utils.js'; +import { config } from 'src/config.js'; +import 'src/prebid.js'; + +let expect = require('chai').expect; + +describe('consentManagementGpp', function () { + describe('setConsentConfig tests:', function () { + describe('empty setConsentConfig value', function () { + beforeEach(function () { + sinon.stub(utils, 'logInfo'); + sinon.stub(utils, 'logWarn'); + }); + + afterEach(function () { + utils.logInfo.restore(); + utils.logWarn.restore(); + config.resetConfig(); + resetConsentData(); + }); + + it('should use system default values', function () { + setConsentConfig({ + gpp: {} + }); + expect(userCMP).to.be.equal('iab'); + expect(consentTimeout).to.be.equal(10000); + sinon.assert.callCount(utils.logInfo, 3); + }); + + it('should exit consent manager if config is not an object', function () { + setConsentConfig(''); + expect(userCMP).to.be.undefined; + sinon.assert.calledOnce(utils.logWarn); + }); + + it('should exit consentManagement module if config is "undefined"', function () { + setConsentConfig(undefined); + expect(userCMP).to.be.undefined; + sinon.assert.calledOnce(utils.logWarn); + }); + + it('should not produce any consent metadata', function () { + setConsentConfig(undefined) + let consentMetadata = gppDataHandler.getConsentMeta(); + expect(consentMetadata).to.be.undefined; + sinon.assert.calledOnce(utils.logWarn); + }) + + it('should immediately look up consent data', () => { + setConsentConfig({ + gpp: { + cmpApi: 'invalid' + } + }); + expect(gppDataHandler.ready).to.be.true; + }) + }); + + describe('valid setConsentConfig value', function () { + afterEach(function () { + config.resetConfig(); + }); + + it('results in all user settings overriding system defaults', function () { + let allConfig = { + gpp: { + cmpApi: 'iab', + timeout: 7500 + } + }; + + setConsentConfig(allConfig); + expect(userCMP).to.be.equal('iab'); + expect(consentTimeout).to.be.equal(7500); + }); + + it('should recognize config.gpp, with default cmpApi and timeout', function () { + setConsentConfig({ + gpp: {} + }); + + expect(userCMP).to.be.equal('iab'); + expect(consentTimeout).to.be.equal(10000); + }); + + it('should enable gppDataHandler', () => { + setConsentConfig({ + gpp: {} + }); + expect(gppDataHandler.enabled).to.be.true; + }); + }); + + describe('static consent string setConsentConfig value', () => { + afterEach(() => { + config.resetConfig(); + }); + + it('results in user settings overriding system defaults for v2 spec', () => { + let staticConfig = { + gpp: { + cmpApi: 'static', + timeout: 7500, + consentData: { + applicableSections: [7], + gppString: 'ABCDEFG1234', + gppVersion: 1, + sectionId: 3, + sectionList: [] + } + } + }; + + setConsentConfig(staticConfig); + expect(userCMP).to.be.equal('static'); + expect(consentTimeout).to.be.equal(0); // should always return without a timeout when config is used + const consent = gppDataHandler.getConsentData(); + expect(consent.gppString).to.eql(staticConfig.gpp.consentData.gppString); + expect(consent.fullGppData).to.eql(staticConfig.gpp.consentData); + expect(staticConsentData).to.be.equal(staticConfig.gpp.consentData); + }); + }); + }); + + describe('requestBidsHook tests:', function () { + let goodConfig = { + gpp: { + cmpApi: 'iab', + timeout: 7500, + } + }; + + const staticConfig = { + gpp: { + cmpApi: 'static', + timeout: 7500, + consentData: { + gppString: 'abc12345', + applicableSections: [7] + } + } + } + + let didHookReturn; + + beforeEach(resetConsentData); + after(resetConsentData); + + describe('error checks:', function () { + beforeEach(function () { + didHookReturn = false; + sinon.stub(utils, 'logWarn'); + sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + utils.logWarn.restore(); + utils.logError.restore(); + config.resetConfig(); + }); + + it('should throw a warning and return to hooked function when an unknown CMP framework ID is used', function () { + let badCMPConfig = { + gpp: { + cmpApi: 'bad' + } + }; + setConsentConfig(badCMPConfig); + expect(userCMP).to.be.equal(badCMPConfig.gpp.cmpApi); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gppDataHandler.getConsentData(); + + sinon.assert.calledOnce(utils.logWarn); + expect(didHookReturn).to.be.true; + expect(consent).to.be.null; + }); + + it('should call gppDataHandler.setConsentData() when unknown CMP api is used', () => { + setConsentConfig({ + gpp: { + cmpApi: 'invalid' + } + }); + let hookRan = false; + requestBidsHook(() => { + hookRan = true; + }, {}); + expect(hookRan).to.be.true; + expect(gppDataHandler.ready).to.be.true; + }) + + it('should throw proper errors when CMP is not found', function () { + setConsentConfig(goodConfig); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gppDataHandler.getConsentData(); + // throw 2 errors; one for no bidsBackHandler and for CMP not being found (this is an error due to gdpr config) + sinon.assert.calledTwice(utils.logError); + expect(didHookReturn).to.be.false; + expect(consent).to.be.null; + expect(gppDataHandler.ready).to.be.true; + }); + + it('should not trip when adUnits have no size', () => { + setConsentConfig(staticConfig); + let ran = false; + requestBidsHook(() => { + ran = true; + }, { + adUnits: [{ + code: 'test', + mediaTypes: { + video: {} + } + }] + }); + return gppDataHandler.promise.then(() => { + expect(ran).to.be.true; + }); + }); + + it('should continue the auction immediately, without consent data, if timeout is 0', (done) => { + setConsentConfig({ + gpp: { + cmpApi: 'iab', + timeout: 0 + } + }); + window.__gpp = function () {}; + try { + requestBidsHook(() => { + const consent = gppDataHandler.getConsentData(); + expect(consent.applicableSections).to.deep.equal([]); + expect(consent.gppString).to.be.undefined; + done(); + }, {}) + } finally { + delete window.__gpp; + } + }); + }); + + describe('already known consentData:', function () { + let cmpStub = sinon.stub(); + + function mockCMP(cmpResponse) { + return function (...args) { + if (args[0] === 'addEventListener') { + args[1](({ + eventName: 'sectionChange' + })); + } else if (args[0] === 'getGPPData') { + return cmpResponse; + } + } + } + + beforeEach(function () { + didHookReturn = false; + window.__gpp = function () {}; + }); + + afterEach(function () { + config.resetConfig(); + cmpStub.restore(); + delete window.__gpp; + resetConsentData(); + }); + + it('should bypass CMP and simply use previously stored consentData', function () { + let testConsentData = { + applicableSections: [7], + gppString: 'xyz', + }; + + cmpStub = sinon.stub(window, '__gpp').callsFake(mockCMP(testConsentData)); + setConsentConfig(goodConfig); + requestBidsHook(() => {}, {}); + cmpStub.reset(); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gppDataHandler.getConsentData(); + + expect(didHookReturn).to.be.true; + expect(consent.gppString).to.equal(testConsentData.gppString); + expect(consent.applicableSections).to.deep.equal(testConsentData.applicableSections); + sinon.assert.notCalled(cmpStub); + }); + }); + + describe('iframe tests', function () { + let cmpPostMessageCb = () => {}; + let stringifyResponse; + + function createIFrameMarker(frameName) { + let ifr = document.createElement('iframe'); + ifr.width = 0; + ifr.height = 0; + ifr.name = frameName; + document.body.appendChild(ifr); + return ifr; + } + + function creatCmpMessageHandler(prefix, returnEvtValue, returnGPPValue) { + return function (event) { + if (event && event.data) { + let data = event.data; + if (data[`${prefix}Call`]) { + let callId = data[`${prefix}Call`].callId; + let response; + if (data[`${prefix}Call`].command === 'addEventListener') { + response = { + [`${prefix}Return`]: { + callId, + returnValue: returnEvtValue, + success: true + } + } + } else if (data[`${prefix}Call`].command === 'getGPPData') { + response = { + [`${prefix}Return`]: { + callId, + returnValue: returnGPPValue, + success: true + } + } + } + event.source.postMessage(stringifyResponse ? JSON.stringify(response) : response, '*'); + } + } + } + } + + function testIFramedPage(testName, messageFormatString, tarConsentString, tarSections) { + it(`should return the consent string from a postmessage + addEventListener response - ${testName}`, (done) => { + stringifyResponse = messageFormatString; + setConsentConfig(goodConfig); + requestBidsHook(() => { + let consent = gppDataHandler.getConsentData(); + sinon.assert.notCalled(utils.logError); + expect(consent.gppString).to.equal(tarConsentString); + expect(consent.applicableSections).to.deep.equal(tarSections); + done(); + }, {}); + }); + } + + beforeEach(function () { + sinon.stub(utils, 'logError'); + sinon.stub(utils, 'logWarn'); + }); + + afterEach(function () { + utils.logError.restore(); + utils.logWarn.restore(); + config.resetConfig(); + resetConsentData(); + }); + + describe('v2 CMP workflow for iframe pages:', function () { + stringifyResponse = false; + let ifr2 = null; + + beforeEach(function () { + ifr2 = createIFrameMarker('__gppLocator'); + cmpPostMessageCb = creatCmpMessageHandler('__gpp', { + eventName: 'sectionChange' + }, { + gppString: 'abc12345234', + applicableSections: [7] + }); + window.addEventListener('message', cmpPostMessageCb, false); + }); + + afterEach(function () { + delete window.__gpp; // deletes the local copy made by the postMessage CMP call function + document.body.removeChild(ifr2); + window.removeEventListener('message', cmpPostMessageCb); + }); + + testIFramedPage('with/JSON response', false, 'abc12345234', [7]); + testIFramedPage('with/String response', true, 'abc12345234', [7]); + }); + }); + + describe('direct calls to CMP API tests', function () { + let cmpStub = sinon.stub(); + + beforeEach(function () { + didHookReturn = false; + sinon.stub(utils, 'logError'); + sinon.stub(utils, 'logWarn'); + }); + + afterEach(function () { + config.resetConfig(); + cmpStub.restore(); + utils.logError.restore(); + utils.logWarn.restore(); + resetConsentData(); + }); + + describe('v2 CMP workflow for normal pages:', function () { + beforeEach(function () { + window.__gpp = function () {}; + }); + + afterEach(function () { + delete window.__gpp; + }); + + it('performs lookup check and stores consentData for a valid existing user', function () { + let testConsentData = { + gppString: 'abc12345234', + applicableSections: [7] + }; + cmpStub = sinon.stub(window, '__gpp').callsFake((...args) => { + if (args[0] === 'addEventListener') { + args[1]({ + eventName: 'sectionChange' + }); + } else if (args[0] === 'getGPPData') { + return testConsentData; + } + }); + + setConsentConfig(goodConfig); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gppDataHandler.getConsentData(); + sinon.assert.notCalled(utils.logError); + expect(didHookReturn).to.be.true; + expect(consent.gppString).to.equal(testConsentData.gppString); + expect(consent.applicableSections).to.deep.equal(testConsentData.applicableSections); + }); + + it('produces gdpr metadata', function () { + let testConsentData = { + gppString: 'abc12345234', + applicableSections: [7] + }; + cmpStub = sinon.stub(window, '__gpp').callsFake((...args) => { + if (args[0] === 'addEventListener') { + args[1]({ + eventName: 'sectionChange' + }); + } else if (args[0] === 'getGPPData') { + return testConsentData; + } + }); + + setConsentConfig(goodConfig); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consentMeta = gppDataHandler.getConsentMeta(); + sinon.assert.notCalled(utils.logError); + expect(consentMeta.generatedAt).to.be.above(1644367751709); + }); + + it('throws an error when processCmpData check fails + does not call requestBids callback', function () { + let testConsentData = {}; + let bidsBackHandlerReturn = false; + + cmpStub = sinon.stub(window, '__gpp').callsFake((...args) => { + if (args[0] === 'addEventListener') { + args[1]({ + eventName: 'sectionChange' + }); + } else if (args[0] === 'getGPPData') { + return testConsentData; + } + }); + + setConsentConfig(goodConfig); + + sinon.assert.notCalled(utils.logWarn); + sinon.assert.notCalled(utils.logError); + + [utils.logWarn, utils.logError].forEach((stub) => stub.reset()); + + requestBidsHook(() => { + didHookReturn = true; + }, { + bidsBackHandler: () => bidsBackHandlerReturn = true + }); + let consent = gppDataHandler.getConsentData(); + + sinon.assert.calledOnce(utils.logError); + sinon.assert.notCalled(utils.logWarn); + expect(didHookReturn).to.be.false; + expect(bidsBackHandlerReturn).to.be.true; + expect(consent).to.be.null; + expect(gppDataHandler.ready).to.be.true; + }); + + describe('when proper consent is not available', () => { + let gppStub; + + function runAuction() { + setConsentConfig({ + gpp: { + cmpApi: 'iab', + timeout: 10, + } + }); + return new Promise((resolve, reject) => { + requestBidsHook(() => { + didHookReturn = true; + }, {}); + setTimeout(() => didHookReturn ? resolve() : reject(new Error('Auction did not run')), 20); + }) + } + + function mockGppCmp(gppdata) { + gppStub.callsFake((api, cb) => { + if (api === 'addEventListener') { + // eslint-disable-next-line standard/no-callback-literal + cb({ + pingData: { + cmpStatus: 'loaded' + } + }, true); + } + if (api === 'getGPPData') { + return gppdata; + } + }); + } + + beforeEach(() => { + gppStub = sinon.stub(window, '__gpp'); + }); + + afterEach(() => { + gppStub.restore(); + }) + + it('should continue auction with null consent when CMP is unresponsive', () => { + return runAuction().then(() => { + const consent = gppDataHandler.getConsentData(); + expect(consent.applicableSections).to.deep.equal([]); + expect(consent.gppString).to.be.undefined; + expect(gppDataHandler.ready).to.be.true; + }); + }); + + it('should use consent provided by events other than sectionChange', () => { + mockGppCmp({ + gppString: 'mock-consent-string', + applicableSections: [7] + }); + return runAuction().then(() => { + const consent = gppDataHandler.getConsentData(); + expect(consent.applicableSections).to.deep.equal([7]); + expect(consent.gppString).to.equal('mock-consent-string'); + expect(gppDataHandler.ready).to.be.true; + }); + }); + }); + }); + }); + }); +}); From b4688316e76d69590c5da46dbec5b47cedea04f4 Mon Sep 17 00:00:00 2001 From: Andrew Slagle <42588549+spotxslagle@users.noreply.github.com> Date: Wed, 21 Dec 2022 10:53:05 -0700 Subject: [PATCH 541/569] Magnite Analytics Adapter : data deletion function (#9351) * add onDeletionRequest functionality to Magnite adapter * Magnite add onDataDeletionRequest unit testing --- modules/magniteAnalyticsAdapter.js | 10 +++- .../modules/magniteAnalyticsAdapter_spec.js | 50 +++++++++++++------ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/modules/magniteAnalyticsAdapter.js b/modules/magniteAnalyticsAdapter.js index 99f384e9eff..c6911897e84 100644 --- a/modules/magniteAnalyticsAdapter.js +++ b/modules/magniteAnalyticsAdapter.js @@ -643,7 +643,15 @@ magniteAdapter.disableAnalytics = function () { accountId = undefined; resetConfs(); magniteAdapter.originDisableAnalytics(); -} +}; + +magniteAdapter.onDataDeletionRequest = function () { + if (storage.localStorageIsEnabled()) { + storage.removeDataFromLocalStorage(COOKIE_NAME); + } else { + throw Error('Unable to access local storage, no data deleted'); + } +}; magniteAdapter.MODULE_INITIALIZED_TIME = Date.now(); magniteAdapter.referrerHostname = ''; diff --git a/test/spec/modules/magniteAnalyticsAdapter_spec.js b/test/spec/modules/magniteAnalyticsAdapter_spec.js index 69fd7794ced..3606b4b4550 100644 --- a/test/spec/modules/magniteAnalyticsAdapter_spec.js +++ b/test/spec/modules/magniteAnalyticsAdapter_spec.js @@ -306,7 +306,7 @@ const ANALYTICS_MESSAGE = { describe('magnite analytics adapter', function () { let sandbox; let clock; - let getDataFromLocalStorageStub, setDataInLocalStorageStub, localStorageIsEnabledStub; + let getDataFromLocalStorageStub, setDataInLocalStorageStub, localStorageIsEnabledStub, removeDataFromLocalStorageStub; let gptSlot0; let gptSlotRenderEnded0; beforeEach(function () { @@ -325,6 +325,7 @@ describe('magnite analytics adapter', function () { getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); setDataInLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage'); localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); + removeDataFromLocalStorageStub = sinon.stub(storage, 'removeDataFromLocalStorage') sandbox = sinon.sandbox.create(); localStorageIsEnabledStub.returns(true); @@ -355,6 +356,7 @@ describe('magnite analytics adapter', function () { getDataFromLocalStorageStub.restore(); setDataInLocalStorageStub.restore(); localStorageIsEnabledStub.restore(); + removeDataFromLocalStorageStub.restore(); magniteAdapter.disableAnalytics(); }); @@ -1932,19 +1934,21 @@ describe('magnite analytics adapter', function () { }); }); - it('getHostNameFromReferer correctly grabs hostname from an input URL', function () { - let inputUrl = 'https://www.prebid.org/some/path?pbjs_debug=true'; - expect(getHostNameFromReferer(inputUrl)).to.equal('www.prebid.org'); - inputUrl = 'https://www.prebid.com/some/path?pbjs_debug=true'; - expect(getHostNameFromReferer(inputUrl)).to.equal('www.prebid.com'); - inputUrl = 'https://prebid.org/some/path?pbjs_debug=true'; - expect(getHostNameFromReferer(inputUrl)).to.equal('prebid.org'); - inputUrl = 'http://xn--p8j9a0d9c9a.xn--q9jyb4c/'; - expect(typeof getHostNameFromReferer(inputUrl)).to.equal('string'); - - // not non-UTF char's in query / path which break if noDecodeWholeURL not set - inputUrl = 'https://prebid.org/search_results/%95x%8Em%92%CA/?category=000'; - expect(getHostNameFromReferer(inputUrl)).to.equal('prebid.org'); + describe('getHostNameFromReferer', () => { + it('correctly grabs hostname from an input URL', function () { + let inputUrl = 'https://www.prebid.org/some/path?pbjs_debug=true'; + expect(getHostNameFromReferer(inputUrl)).to.equal('www.prebid.org'); + inputUrl = 'https://www.prebid.com/some/path?pbjs_debug=true'; + expect(getHostNameFromReferer(inputUrl)).to.equal('www.prebid.com'); + inputUrl = 'https://prebid.org/some/path?pbjs_debug=true'; + expect(getHostNameFromReferer(inputUrl)).to.equal('prebid.org'); + inputUrl = 'http://xn--p8j9a0d9c9a.xn--q9jyb4c/'; + expect(typeof getHostNameFromReferer(inputUrl)).to.equal('string'); + + // not non-UTF char's in query / path which break if noDecodeWholeURL not set + inputUrl = 'https://prebid.org/search_results/%95x%8Em%92%CA/?category=000'; + expect(getHostNameFromReferer(inputUrl)).to.equal('prebid.org'); + }); }); describe(`handle currency conversions`, () => { @@ -1985,4 +1989,22 @@ describe('magnite analytics adapter', function () { expect(bidResponseObj.bidPriceUSD).to.equal(0); }); }); + + describe('onDataDeletionRequest', () => { + it('attempts to delete the magnite cookie when local storage is enabled', () => { + magniteAdapter.onDataDeletionRequest(); + + expect(removeDataFromLocalStorageStub.getCall(0).args[0]).to.equal('mgniSession'); + }); + + it('throws an error if it cannot access the cookie', (done) => { + localStorageIsEnabledStub.returns(false); + try { + magniteAdapter.onDataDeletionRequest(); + } catch (error) { + expect(error.message).to.equal('Unable to access local storage, no data deleted'); + done(); + } + }) + }); }); From d5746c39c437b5e04505cdd75bdef3791eb28566 Mon Sep 17 00:00:00 2001 From: Bill Newman Date: Wed, 21 Dec 2022 19:56:48 +0200 Subject: [PATCH 542/569] Colosuss Bid Adapter: add support First Party Data (#9340) * add video&native traffic colossus ssp * Native obj validation * Native obj validation #2 * Added size field in requests * fixed test * fix merge conflicts * move to 3.0 * move to 3.0 * fix IE11 new URL issue * fix IE11 new URL issue * fix IE11 new URL issue * https for 3.0 * add https test * add ccp and schain features * fix test * sync with upstream, fix conflicts * Update colossussspBidAdapter.js remove commented code * Update colossussspBidAdapter.js lint fix * identity extensions * identity extensions * fix * fix * fix * fix * fix * add tests for user ids * fix * fix * fix * fix * fix * fix * fix * add gdpr support * add gdpr support * id5id support * Update colossussspBidAdapter.js add bidfloor parameter * Update colossussspBidAdapter.js check bidfloor * Update colossussspBidAdapter.js * Update colossussspBidAdapter.js * Update colossussspBidAdapter.js * Update colossussspBidAdapter_spec.js * use floor module * Revert "use floor module" This reverts commit f0c5c248627567e669d8eed4f2bb9a26a857e2ad. * use floor module * update to 5v * fix * add uid2 and bidFloor support * fix * add pbadslot support * fix conflicts * add onBidWon * refactor * add test for onBidWon() * fix * add group_id * Trigger circleci * fix * update user sync * fix window.location * fix test * updates * fix conflict * fix * updates * remove traffic param * add transactionId to request data for colossusssp adapter * Send tid in placements array * update user sync * updated tests * remove changes package-lock file * fix * add First Party Data Co-authored-by: Vladislav Isaiko Co-authored-by: Aiholkin Co-authored-by: Mykhailo Yaremchuk --- modules/colossussspBidAdapter.js | 8 ++ .../modules/colossussspBidAdapter_spec.js | 91 ++++++++++++++++++- 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index 75e73ffda89..082fb0ac4db 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -87,6 +87,11 @@ export const spec = { logMessage(e); } + const firstPartyData = bidderRequest.ortb2 || {}; + const userObj = firstPartyData.user; + const siteObj = firstPartyData.site; + const appObj = firstPartyData.app; + // TODO: does the fallback to window.location make sense? const location = refferLocation || winLocation; let placements = []; @@ -97,6 +102,9 @@ export const spec = { secure: location.protocol === 'https:' ? 1 : 0, host: location.host, page: location.pathname, + userObj, + siteObj, + appObj, placements: placements }; diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index 71e94f0da32..adb9137d892 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -56,6 +56,93 @@ describe('ColossussspAdapter', function () { referer: 'http://www.example.com', reachedTop: true, }, + ortb2: { + app: { + name: 'myappname', + keywords: 'power tools, drills', + content: { + data: [ + { + name: 'www.dataprovider1.com', + ext: { + segtax: 6 + }, + segment: [ + { + id: '687' + }, + { + id: '123' + } + ] + }, + { + name: 'www.dataprovider1.com', + ext: { + segtax: 7 + }, + segment: [ + { + id: '456' + }, + { + id: '789' + } + ] + } + ] + } + }, + site: { + name: 'example', + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'], + page: 'https://page.example.com/here.html', + ref: 'https://ref.example.com', + keywords: 'power tools, drills', + search: 'drill', + content: { + userrating: '4', + data: [{ + name: 'www.dataprovider1.com', + ext: { + segtax: 7, + cids: ['iris_c73g5jq96mwso4d8'] + }, + segment: [ + { id: '687' }, + { id: '123' } + ] + }] + }, + ext: { + data: { + pageType: 'article', + category: 'repair' + } + } + }, + user: { + yob: 1985, + gender: 'm', + keywords: 'a,b', + data: [{ + name: 'dataprovider.com', + ext: { segtax: 4 }, + segment: [ + { id: '1' } + ] + }], + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + }, bids: [bid] } @@ -91,7 +178,7 @@ describe('ColossussspAdapter', function () { it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'ccpa', 'gdpr_consent', 'gdpr_require'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'ccpa', 'gdpr_consent', 'gdpr_require', 'userObj', 'siteObj', 'appObj'); expect(data.deviceWidth).to.be.a('number'); expect(data.deviceHeight).to.be.a('number'); expect(data.language).to.be.a('string'); @@ -132,7 +219,7 @@ describe('ColossussspAdapter', function () { let data = serverRequest.data; expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'ccpa', 'gdpr_consent', 'gdpr_require'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'ccpa', 'gdpr_consent', 'gdpr_require', 'userObj', 'siteObj', 'appObj'); expect(data.deviceWidth).to.be.a('number'); expect(data.deviceHeight).to.be.a('number'); expect(data.language).to.be.a('string'); From 9c3abaea97f282e9e62f3a5b80bcfdbaefc7cb88 Mon Sep 17 00:00:00 2001 From: mjaworskiccx <50406214+mjaworskiccx@users.noreply.github.com> Date: Thu, 22 Dec 2022 20:10:49 +0100 Subject: [PATCH 543/569] Clickonometrics Bid Adapter: gvlid (#9367) * adomain support * adomain support * adomain support * adomain support * adomain support * video params * docs changes * Clickonometrics adapter update * Revert "Revert "Clickonometrics Bid Adapter : add gvlid (#9198)" (#9216)" This reverts commit 6d114e83725b403fadd889202b449de225db7275. * Test fix --- modules/ccxBidAdapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 7c6b0411023..28f3fe3a166 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -5,6 +5,7 @@ import {getStorageManager} from '../src/storageManager.js'; const BIDDER_CODE = 'ccx' const storage = getStorageManager({bidderCode: BIDDER_CODE}); const BID_URL = 'https://delivery.clickonometrics.pl/ortb/prebid/bid' +const GVLID = 773; const SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6] const SUPPORTED_VIDEO_MIMES = ['video/mp4', 'video/x-flv'] const SUPPORTED_VIDEO_PLAYBACK_METHODS = [1, 2, 3, 4] @@ -19,8 +20,7 @@ function _getDeviceObj () { function _getSiteObj (bidderRequest) { let site = {} - // TODO: does the fallback to window.location make sense? - let url = bidderRequest?.refererInfo?.page || window.location.href + let url = bidderRequest?.refererInfo?.page || '' if (url.length > 0) { url = url.split('?')[0] } @@ -140,6 +140,7 @@ function _buildResponse (bid, currency, ttl) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { From e5bcdaa059a3ad675ff3e3dae78423bb558f498e Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 23 Dec 2022 17:14:37 +0000 Subject: [PATCH 544/569] Prebid 7.30.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c903795e59d..51491f04fbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.30.0-pre", + "version": "7.30.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 490303874f7..6ffdac7d3a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.30.0-pre", + "version": "7.30.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 4e45ba34c76ad03b417942320c376749c9f96b44 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 23 Dec 2022 17:14:37 +0000 Subject: [PATCH 545/569] Increment version to 7.31.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 51491f04fbe..7de32ef7102 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.30.0", + "version": "7.31.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 6ffdac7d3a6..f96909cc6b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.30.0", + "version": "7.31.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 684691bb69263fcdda1d31b3fabe3db859e609b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 09:29:25 -0500 Subject: [PATCH 546/569] Bump parse-url from 7.0.2 to 8.1.0 (#9372) Bumps [parse-url](https://github.com/IonicaBizau/parse-url) from 7.0.2 to 8.1.0. - [Release notes](https://github.com/IonicaBizau/parse-url/releases) - [Commits](https://github.com/IonicaBizau/parse-url/compare/7.0.2...8.1.0) --- updated-dependencies: - dependency-name: parse-url dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 84 ++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7de32ef7102..2b703493403 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "7.28.0-pre", + "version": "7.31.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -8534,9 +8534,9 @@ } }, "node_modules/documentation": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/documentation/-/documentation-14.0.0.tgz", - "integrity": "sha512-4AwFzdiseEdtqR0KKLrruIQ5fvh7n5epg47P0ZyOidA5Fes5am+6xjqkDECHPwcv4pxJ6zITaHwzCoGblP0+JQ==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/documentation/-/documentation-14.0.1.tgz", + "integrity": "sha512-Y/brACCE3sNnDJPFiWlhXrqGY+NelLYVZShLGse5bT1KdohP4JkPf5T2KNq1YWhIEbDYl/1tebRLC0WYbPQxVw==", "dev": true, "dependencies": { "@babel/core": "^7.18.10", @@ -8548,7 +8548,7 @@ "chokidar": "^3.5.3", "diff": "^5.1.0", "doctrine-temporary-fork": "2.1.0", - "git-url-parse": "^12.0.0", + "git-url-parse": "^13.1.0", "github-slugger": "1.4.0", "glob": "^8.0.3", "globals-docs": "^2.4.1", @@ -11261,22 +11261,22 @@ } }, "node_modules/git-up": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-6.0.0.tgz", - "integrity": "sha512-6RUFSNd1c/D0xtGnyWN2sxza2bZtZ/EmI9448n6rCZruFwV/ezeEn2fJP7XnUQGwf0RAtd/mmUCbtH6JPYA2SA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", + "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", "dev": true, "dependencies": { "is-ssh": "^1.4.0", - "parse-url": "^7.0.2" + "parse-url": "^8.1.0" } }, "node_modules/git-url-parse": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-12.0.0.tgz", - "integrity": "sha512-I6LMWsxV87vysX1WfsoglXsXg6GjQRKq7+Dgiseo+h0skmp5Hp2rzmcEIRQot9CPA+uzU7x1x7jZdqvTFGnB+Q==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", + "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", "dev": true, "dependencies": { - "git-up": "^6.0.0" + "git-up": "^7.0.0" } }, "node_modules/github-slugger": { @@ -19420,24 +19420,21 @@ } }, "node_modules/parse-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz", - "integrity": "sha512-qOpH55/+ZJ4jUu/oLO+ifUKjFPNZGfnPJtzvGzKN/4oLMil5m9OH4VpOj6++9/ytJcfks4kzH2hhi87GL/OU9A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", + "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", "dev": true, "dependencies": { "protocols": "^2.0.0" } }, "node_modules/parse-url": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-7.0.2.tgz", - "integrity": "sha512-PqO4Z0eCiQ08Wj6QQmrmp5YTTxpYfONdOEamrtvK63AmzXpcavIVQubGHxOEwiIoDZFb8uDOoQFS0NCcjqIYQg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", + "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", "dev": true, "dependencies": { - "is-ssh": "^1.4.0", - "normalize-url": "^6.1.0", - "parse-path": "^5.0.0", - "protocols": "^2.0.1" + "parse-path": "^7.0.0" } }, "node_modules/parseurl": { @@ -31782,9 +31779,9 @@ } }, "documentation": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/documentation/-/documentation-14.0.0.tgz", - "integrity": "sha512-4AwFzdiseEdtqR0KKLrruIQ5fvh7n5epg47P0ZyOidA5Fes5am+6xjqkDECHPwcv4pxJ6zITaHwzCoGblP0+JQ==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/documentation/-/documentation-14.0.1.tgz", + "integrity": "sha512-Y/brACCE3sNnDJPFiWlhXrqGY+NelLYVZShLGse5bT1KdohP4JkPf5T2KNq1YWhIEbDYl/1tebRLC0WYbPQxVw==", "dev": true, "requires": { "@babel/core": "^7.18.10", @@ -31797,7 +31794,7 @@ "chokidar": "^3.5.3", "diff": "^5.1.0", "doctrine-temporary-fork": "2.1.0", - "git-url-parse": "^12.0.0", + "git-url-parse": "^13.1.0", "github-slugger": "1.4.0", "glob": "^8.0.3", "globals-docs": "^2.4.1", @@ -33988,22 +33985,22 @@ } }, "git-up": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-6.0.0.tgz", - "integrity": "sha512-6RUFSNd1c/D0xtGnyWN2sxza2bZtZ/EmI9448n6rCZruFwV/ezeEn2fJP7XnUQGwf0RAtd/mmUCbtH6JPYA2SA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", + "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", "dev": true, "requires": { "is-ssh": "^1.4.0", - "parse-url": "^7.0.2" + "parse-url": "^8.1.0" } }, "git-url-parse": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-12.0.0.tgz", - "integrity": "sha512-I6LMWsxV87vysX1WfsoglXsXg6GjQRKq7+Dgiseo+h0skmp5Hp2rzmcEIRQot9CPA+uzU7x1x7jZdqvTFGnB+Q==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", + "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", "dev": true, "requires": { - "git-up": "^6.0.0" + "git-up": "^7.0.0" } }, "github-slugger": { @@ -40244,24 +40241,21 @@ "dev": true }, "parse-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz", - "integrity": "sha512-qOpH55/+ZJ4jUu/oLO+ifUKjFPNZGfnPJtzvGzKN/4oLMil5m9OH4VpOj6++9/ytJcfks4kzH2hhi87GL/OU9A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", + "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", "dev": true, "requires": { "protocols": "^2.0.0" } }, "parse-url": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-7.0.2.tgz", - "integrity": "sha512-PqO4Z0eCiQ08Wj6QQmrmp5YTTxpYfONdOEamrtvK63AmzXpcavIVQubGHxOEwiIoDZFb8uDOoQFS0NCcjqIYQg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", + "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", "dev": true, "requires": { - "is-ssh": "^1.4.0", - "normalize-url": "^6.1.0", - "parse-path": "^5.0.0", - "protocols": "^2.0.1" + "parse-path": "^7.0.0" } }, "parseurl": { From a5ea3097aacb6052a030220bdf87131d4ae5d59b Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 26 Dec 2022 07:34:57 -0700 Subject: [PATCH 547/569] Prebid core: filter adUnits (by `adUnitCodes`) before sending them to RTD and FPD modules (#9355) --- src/prebid.js | 15 +++++++-------- test/spec/unit/pbjs_api_spec.js | 11 +++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/prebid.js b/src/prebid.js index a4abceb01d5..3fb131fb02c 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -629,6 +629,13 @@ $$PREBID_GLOBAL$$.requestBids = (function() { events.emit(REQUEST_BIDS); const cbTimeout = timeout || config.getConfig('bidderTimeout'); logInfo('Invoking $$PREBID_GLOBAL$$.requestBids', arguments); + if (adUnitCodes && adUnitCodes.length) { + // if specific adUnitCodes supplied filter adUnits for those codes + adUnits = adUnits.filter(unit => includes(adUnitCodes, unit.code)); + } else { + // otherwise derive adUnitCodes from adUnits + adUnitCodes = adUnits && adUnits.map(unit => unit.code); + } const ortb2Fragments = { global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, cfg.ortb2]).filter(([_, ortb2]) => ortb2 != null)) @@ -661,14 +668,6 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout: const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []); adUnits = useMetrics(metrics).measureTime('requestBids.validate', () => checkAdUnitSetup(adUnits)); - if (adUnitCodes && adUnitCodes.length) { - // if specific adUnitCodes supplied filter adUnits for those codes - adUnits = adUnits.filter(unit => includes(adUnitCodes, unit.code)); - } else { - // otherwise derive adUnitCodes from adUnits - adUnitCodes = adUnits && adUnits.map(unit => unit.code); - } - function auctionDone(bids, timedOut, auctionId) { if (typeof bidsBackHandler === 'function') { try { diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 9d8ea70e200..ae9ea09908a 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1810,6 +1810,16 @@ describe('Unit: Prebid Module', function () { }) }); + it('filtering adUnits by adUnitCodes', () => { + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [{code: 'one'}, {code: 'two'}], + adUnitCodes: 'two' + }); + sinon.assert.calledWith(startAuctionStub, sinon.match({ + adUnits: [{code: 'two'}] + })); + }); + it('passing bidder-specific FPD as ortb2Fragments.bidder', () => { configObj.setBidderConfig({ bidders: ['bidderA', 'bidderC'], @@ -1869,6 +1879,7 @@ describe('Unit: Prebid Module', function () { mediaTypes: {banner: {sizes: [[300, 250]]}}, bids: [{bidder: 'bd'}] }], + adUnitCodes: ['au'], ortb2Fragments }); sinon.assert.calledWith(newAuctionStub, sinon.match({ From fdab2f10459bb81fb61d4441a1c64ff749791d6b Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Mon, 26 Dec 2022 06:36:12 -0800 Subject: [PATCH 548/569] Prebid Server: Include adUnitCode in PBS Adapter Requests (#9337) * changes * added support to pass adunitcode to pbs * updated comment * removed console log statements * addressed feedback --- .../pbsExtensions/processors/adUnitCode.js | 13 +++++++++++ libraries/pbsExtensions/processors/pbs.js | 5 ++++ .../prebidServerBidAdapter/ortbConverter.js | 1 + .../pbsExtensions/adUnitCode_spec.js | 23 +++++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 libraries/pbsExtensions/processors/adUnitCode.js create mode 100644 test/spec/ortbConverter/pbsExtensions/adUnitCode_spec.js diff --git a/libraries/pbsExtensions/processors/adUnitCode.js b/libraries/pbsExtensions/processors/adUnitCode.js new file mode 100644 index 00000000000..f936e0f662f --- /dev/null +++ b/libraries/pbsExtensions/processors/adUnitCode.js @@ -0,0 +1,13 @@ +import {deepSetValue} from '../../../src/utils.js'; + +export function setImpAdUnitCode(imp, bidRequest) { + const adUnitCode = bidRequest.adUnitCode; + + if (adUnitCode) { + deepSetValue( + imp, + `ext.prebid.adunitcode`, + adUnitCode + ); + } +} diff --git a/libraries/pbsExtensions/processors/pbs.js b/libraries/pbsExtensions/processors/pbs.js index 56a2a391c76..0ed2d12fad8 100644 --- a/libraries/pbsExtensions/processors/pbs.js +++ b/libraries/pbsExtensions/processors/pbs.js @@ -3,6 +3,7 @@ import {deepAccess, isPlainObject, isStr, mergeDeep} from '../../../src/utils.js import {extPrebidMediaType} from './mediaType.js'; import {setRequestExtPrebidAliases} from './aliases.js'; import {setImpBidParams} from './params.js'; +import {setImpAdUnitCode} from './adUnitCode.js'; import {setRequestExtPrebid, setRequestExtPrebidChannel} from './requestExtPrebid.js'; import {setBidResponseVideoCache} from './video.js'; @@ -26,6 +27,10 @@ export const PBS_PROCESSORS = { // sets bid ext.prebid.bidder.[bidderCode] with bidRequest.params, passed through transformBidParams if necessary fn: setImpBidParams }, + adUnitCode: { + // sets bid ext.prebid.adunitcode + fn: setImpAdUnitCode + } }, [BID_RESPONSE]: { mediaType: { diff --git a/modules/prebidServerBidAdapter/ortbConverter.js b/modules/prebidServerBidAdapter/ortbConverter.js index 0aee261c25d..83335f81bc2 100644 --- a/modules/prebidServerBidAdapter/ortbConverter.js +++ b/modules/prebidServerBidAdapter/ortbConverter.js @@ -246,6 +246,7 @@ export function buildPBSRequest(s2sBidRequest, bidderRequests, adUnits, requeste impIds.add(impId) proxyBidRequests.push({ ...adUnit, + adUnitCode: adUnit.code, ...getDefinedParams(actualBidRequests.values().next().value || {}, ['userId', 'userIdAsEids', 'schain']), pbsData: {impId, actualBidRequests, adUnit} }); diff --git a/test/spec/ortbConverter/pbsExtensions/adUnitCode_spec.js b/test/spec/ortbConverter/pbsExtensions/adUnitCode_spec.js new file mode 100644 index 00000000000..e17f9e856b0 --- /dev/null +++ b/test/spec/ortbConverter/pbsExtensions/adUnitCode_spec.js @@ -0,0 +1,23 @@ +import {setImpAdUnitCode} from '../../../../libraries/pbsExtensions/processors/adUnitCode.js'; + +describe('pbjs -> ortb adunit code to imp[].ext.prebid.adunitcode', () => { + function setImp(bidRequest) { + const imp = {}; + setImpAdUnitCode(imp, bidRequest); + return imp; + } + + it('it sets adunitcode in ext.prebid.adunitcode when adUnitCode is present', () => { + expect(setImp({bidder: 'mockBidder', adUnitCode: 'mockAdUnit'})).to.eql({ + 'ext': { + 'prebid': { + 'adunitcode': 'mockAdUnit' + } + } + }) + }); + + it('does not set adunitcode in ext.prebid.adunitcode if adUnit is undefined', () => { + expect(setImp({bidder: 'mockBidder'})).to.eql({}); + }); +}); From 5a424e4b992aabad0d5ffa0b87d66fc078790f5e Mon Sep 17 00:00:00 2001 From: couchcrew-thomas Date: Mon, 26 Dec 2022 15:47:35 +0100 Subject: [PATCH 549/569] Feedad Bid Adapter: fixed usersync parsing (#9353) * added file scaffold * added isBidRequestValid implementation * added local prototype of ad integration * added implementation for placement ID validation * fixed video context filter * applied lint to feedad bid adapter * added unit test for bid request validation * added buildRequest unit test * added unit tests for timeout and bid won callbacks * updated bid request to FeedAd API * added parsing of feedad api bid response * added transmisison of tracking events to FeedAd Api * code cleanup * updated feedad unit tests for buildRequest method * added unit tests for event tracking implementation * added unit test for interpretResponse method * added adapter documentation * added dedicated feedad example page * updated feedad adapter to use live system * updated FeedAd adapter placement ID regex * removed groups from FeedAd adapter placement ID regex * removed dedicated feedad example page * updated imports in FeedAd adapter file to use relative paths * updated FeedAd adapter unit test to use sinon.useFakeXMLHttpRequest() * added GDPR fields to the FeedAd bid request * removed video from supported media types of the FeedAd adapter * increased version code of FeedAd adapter to 1.0.2 * removed unnecessary check of bidder request * fixed unit test testing for old FeedAd version * removed video media type example from documentation file * added gvlid to FeedAd adapter * added decoration parameter to adapter documentation * added pass through of additional bid parameters * added user syncs to FeedAd bid adapter * increased FeedAd bid adapter version * lint pass over FeedAd bid adapter * fixed parsing of user syncs from server response * increased FeedAd bid adapter version * fixed version code in test file --- modules/feedadBidAdapter.js | 14 ++++-- test/spec/modules/feedadBidAdapter_spec.js | 51 ++++++++++++++++------ 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/modules/feedadBidAdapter.js b/modules/feedadBidAdapter.js index 0ab6def38ae..ef2e57c553f 100644 --- a/modules/feedadBidAdapter.js +++ b/modules/feedadBidAdapter.js @@ -7,7 +7,7 @@ import {ajax} from '../src/ajax.js'; * Version of the FeedAd bid adapter * @type {string} */ -const VERSION = '1.0.3'; +const VERSION = '1.0.4'; /** * @typedef {object} FeedAdApiBidRequest @@ -294,12 +294,20 @@ function trackingHandlerFactory(klass) { /** * Reads the user syncs off the server responses and converts them into Prebid.JS format * @param {SyncOptions} syncOptions - * @param {FeedAdApiBidResponse[]} serverResponses + * @param {ServerResponse[]} serverResponses * @param gdprConsent * @param uspConsent */ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { - return serverResponses.map(response => response.ext) + return serverResponses.map(response => { + // validate response format + const ext = deepAccess(response, 'body.ext', []); + if (ext == null) { + return null; + } + return ext; + }) + .filter(ext => ext != null) .flatMap(extension => { // extract user syncs from extension const pixels = syncOptions.pixelEnabled && extension.pixels ? extension.pixels : []; diff --git a/test/spec/modules/feedadBidAdapter_spec.js b/test/spec/modules/feedadBidAdapter_spec.js index 6db0b88a9bf..6aed670a563 100644 --- a/test/spec/modules/feedadBidAdapter_spec.js +++ b/test/spec/modules/feedadBidAdapter_spec.js @@ -358,14 +358,20 @@ describe('FeedAdAdapter', function () { const pixelSync2 = {type: 'image', url: 'the pixel url 2'}; const iFrameSync1 = {type: 'iframe', url: 'the iFrame url 1'}; const iFrameSync2 = {type: 'iframe', url: 'the iFrame url 2'}; + const mockServerResponse = (content) => { + if (!(content instanceof Array)) { + content = [content]; + } + return content.map(it => ({body: it})); + }; it('should pass through the syncs out of the extension fields of the server response', function () { - const serverResponse = [{ + const serverResponse = mockServerResponse([{ ext: { pixels: [pixelSync1, pixelSync2], iframes: [iFrameSync1, iFrameSync2], } - }]; + }]); const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse) expect(result).to.deep.equal([ pixelSync1, @@ -376,7 +382,7 @@ describe('FeedAdAdapter', function () { }); it('should concat the syncs of all responses', function () { - const serverResponse = [{ + const serverResponse = mockServerResponse([{ ext: { pixels: [pixelSync1], iframes: [iFrameSync2], @@ -391,7 +397,7 @@ describe('FeedAdAdapter', function () { ext: { pixels: [pixelSync2], } - }]; + }]); const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse); expect(result).to.deep.equal([ pixelSync1, @@ -402,7 +408,7 @@ describe('FeedAdAdapter', function () { }); it('should filter out duplicates', function () { - const serverResponse = [{ + const serverResponse = mockServerResponse([{ ext: { pixels: [pixelSync1, pixelSync1], iframes: [iFrameSync2, iFrameSync2], @@ -411,7 +417,7 @@ describe('FeedAdAdapter', function () { ext: { iframes: [iFrameSync2, iFrameSync2], } - }]; + }]); const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse); expect(result).to.deep.equal([ pixelSync1, @@ -420,12 +426,12 @@ describe('FeedAdAdapter', function () { }); it('should not include iFrame syncs if the option is disabled', function () { - const serverResponse = [{ + const serverResponse = mockServerResponse([{ ext: { pixels: [pixelSync1, pixelSync2], iframes: [iFrameSync1, iFrameSync2], } - }]; + }]); const result = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, serverResponse); expect(result).to.deep.equal([ pixelSync1, @@ -434,12 +440,12 @@ describe('FeedAdAdapter', function () { }); it('should not include pixel syncs if the option is disabled', function () { - const serverResponse = [{ + const serverResponse = mockServerResponse([{ ext: { pixels: [pixelSync1, pixelSync2], iframes: [iFrameSync1, iFrameSync2], } - }]; + }]); const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, serverResponse); expect(result).to.deep.equal([ iFrameSync1, @@ -448,15 +454,34 @@ describe('FeedAdAdapter', function () { }); it('should not include any syncs if the sync options are disabled or missing', function () { - const serverResponse = [{ + const serverResponse = mockServerResponse([{ ext: { pixels: [pixelSync1, pixelSync2], iframes: [iFrameSync1, iFrameSync2], } - }]; + }]); const result = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}, serverResponse); expect(result).to.deep.equal([]); }); + + it('should handle empty responses', function () { + const serverResponse = mockServerResponse([]); + const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse) + expect(result).to.deep.equal([]); + }); + + it('should not throw if the server response is weird', function () { + const responses = [ + mockServerResponse(null), + mockServerResponse('null'), + mockServerResponse(1234), + mockServerResponse({}), + mockServerResponse([{}, 123]), + ]; + responses.forEach(it => { + expect(() => spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, it)).not.to.throw; + }); + }); }); describe('event tracking calls', function () { @@ -592,7 +617,7 @@ describe('FeedAdAdapter', function () { prebid_bid_id: bidId, prebid_transaction_id: transactionId, referer, - sdk_version: '1.0.3' + sdk_version: '1.0.4' }; subject(data); expect(server.requests.length).to.equal(1); From f794bce175d49bab6da6435d41335fb2fbf45c9c Mon Sep 17 00:00:00 2001 From: vishal-dw <109065778+vishal-dw@users.noreply.github.com> Date: Mon, 26 Dec 2022 14:49:46 +0000 Subject: [PATCH 550/569] Datawrkz adapter: Using bidRequest.getFloor() method for bid floor (#9366) * New Bid Adapter: datawrkz * New Bid Adapter: datawrkz. Test case formatting * New Bid Adatpter: datawrkz - updated import statements * Datawrkz adapter: Using bidRequest.getFloor() method for bid floor --- modules/datawrkzBidAdapter.js | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/modules/datawrkzBidAdapter.js b/modules/datawrkzBidAdapter.js index 71870d6437d..193a1723403 100644 --- a/modules/datawrkzBidAdapter.js +++ b/modules/datawrkzBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, getBidIdParameter, isArray, getUniqueIdentifierStr, contains } from '../src/utils.js'; +import { deepAccess, getBidIdParameter, isArray, getUniqueIdentifierStr, contains, isFn, isPlainObject } from '../src/utils.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -84,7 +84,7 @@ export const spec = { /* Generate bid request for banner adunit */ function buildBannerRequest(bidRequest, bidderRequest) { - let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + let bidFloor = getBidFloor(bidRequest); let adW = 0; let adH = 0; @@ -135,7 +135,7 @@ function buildNativeRequest(bidRequest, bidderRequest) { let counter = 0; let assets = []; - let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + let bidFloor = getBidFloor(bidRequest); let title = deepAccess(bidRequest, 'mediaTypes.native.title'); if (title && title.len) { @@ -196,7 +196,7 @@ function buildNativeRequest(bidRequest, bidderRequest) { /* Generate bid request for video adunit */ function buildVideoRequest(bidRequest, bidderRequest) { - let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + let bidFloor = getBidFloor(bidRequest); let sizeObj = getVideoAdUnitSize(bidRequest); @@ -414,6 +414,7 @@ function buildBannerResponse(bidRequest, bidResponse) { } let bidSizes = (deepAccess(bidRequest, 'mediaTypes.banner.sizes')) ? deepAccess(bidRequest, 'mediaTypes.banner.sizes') : bidRequest.sizes; bidResponse.requestId = bidRequest.bidId; + bidResponse.auctionId = bidRequest.auctionId; bidResponse.transactionId = bidRequest.transactionId; bidResponse.placementCode = placementCode; bidResponse.cpm = responseCPM; @@ -455,6 +456,7 @@ function buildNativeResponse(bidRequest, response) { return; } bidResponse.requestId = bidRequest.bidId; + bidResponse.auctionId = bidRequest.auctionId; bidResponse.transactionId = bidRequest.transactionId; bidResponse.placementCode = placementCode; bidResponse.cpm = responseCPM; @@ -507,6 +509,7 @@ function buildVideoResponse(bidRequest, response) { let context = bidRequest.mediaTypes.video.context; bidResponse.requestId = bidRequest.bidId; + bidResponse.auctionId = bidRequest.auctionId; bidResponse.transactionId = bidRequest.transactionId; bidResponse.placementCode = placementCode; bidResponse.cpm = responseCPM; @@ -630,4 +633,21 @@ function getNativeAssestObj(obj, assets) { } } +// BUILD REQUESTS: BIDFLOORS +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return (bid.params.bidfloor) ? bid.params.bidfloor : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); From b6f9b2effb5c9a51eacc1398f69c5001ab87a14e Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Mon, 26 Dec 2022 22:35:09 +0200 Subject: [PATCH 551/569] Adkernel Bid Adapter: bidbuddy.co.in alias (#9375) --- modules/adkernelBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index b0449418a87..5736c7c19ba 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -97,7 +97,8 @@ export const spec = { {code: 'motionspots'}, {code: 'sonic_twist'}, {code: 'displayioads'}, - {code: 'rtbdemand_com'} + {code: 'rtbdemand_com'}, + {code: 'bidbuddy'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], From 86ed8f0057fa3cb611f3fc0855244d5981d0a2b0 Mon Sep 17 00:00:00 2001 From: denys-berzoy-confiant <50574764+denys-berzoy-confiant@users.noreply.github.com> Date: Tue, 27 Dec 2022 05:58:33 +0300 Subject: [PATCH 552/569] Confiant RTD Module : initial release (#9325) * Confiant's RTD Provider Module * Confiant RTD Module: - updated script injection code to current standard - added Confiant as an exclusion to load external JS * Confiant RTD Provider: - additional param for enabling BillingEvent added - docs updated - outdated unit test removed Co-authored-by: Patrick McCann --- modules/.submodules.json | 1 + modules/confiantRtdProvider.js | 128 ++++++++++++++++++ modules/confiantRtdProvider.md | 45 ++++++ src/adloader.js | 3 +- test/spec/modules/confiantRtdProvider_spec.js | 107 +++++++++++++++ 5 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 modules/confiantRtdProvider.js create mode 100644 modules/confiantRtdProvider.md create mode 100644 test/spec/modules/confiantRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index c3016914f1b..deeee91e247 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -58,6 +58,7 @@ "blueconicRtdProvider", "browsiRtdProvider", "captifyRtdProvider", + "confiantRtdProvider", "dgkeywordRtdProvider", "geoedgeRtdProvider", "hadronRtdProvider", diff --git a/modules/confiantRtdProvider.js b/modules/confiantRtdProvider.js new file mode 100644 index 00000000000..4a524b92d75 --- /dev/null +++ b/modules/confiantRtdProvider.js @@ -0,0 +1,128 @@ +/** + * This module provides comprehensive detection of security, quality, and privacy threats by Confiant Inc, + * the industry leader in real-time detecting and blocking of bad ads + * + * The {@link module:modules/realTimeData} module is required + * The module will inject a Confiant Inc. script into the page to monitor ad impressions + * @module modules/cleanioRtdProvider + * @requires module:modules/realTimeData + */ + +import { submodule } from '../src/hook.js'; +import { logError, generateUUID } from '../src/utils.js'; +import { loadExternalScript } from '../src/adloader.js'; +import * as events from '../src/events.js'; +import CONSTANTS from '../src/constants.json'; + +/** + * Injects the Confiant Inc. configuration script into the page, based on proprtyId provided + * @param {string} propertyId + */ +function injectConfigScript(propertyId) { + const scriptSrc = `https://cdn.confiant-integrations.net/${propertyId}/gpt_and_prebid/config.js`; + + loadExternalScript(scriptSrc, 'confiant', () => {}); +} + +/** + * Set up page with Confiant integration + * @param {Object} config + */ +function setupPage(config) { + const propertyId = config?.params?.propertyId; + if (!propertyId) { + logError('Confiant pbjs module: no propertyId provided'); + return false; + } + + const confiant = window.confiant || Object.create(null); + confiant[propertyId] = confiant[propertyId] || Object.create(null); + confiant[propertyId].clientSettings = confiant[propertyId].clientSettings || Object.create(null); + confiant[propertyId].clientSettings.isMGBL = true; + confiant[propertyId].clientSettings.prebidExcludeBidders = config?.params?.prebidExcludeBidders; + confiant[propertyId].clientSettings.prebidNameSpace = config?.params?.prebidNameSpace; + + if (config?.params?.shouldEmitBillableEvent) { + if (window.frames['cnftComm']) { + subscribeToConfiantCommFrame(window); + } else { + setUpMutationObserver(); + } + } + + injectConfigScript(propertyId); + return true; +} + +/** + * Subscribe to window's message events to report Billable events + * @param {Window} targetWindow window instance to subscribe to + */ +function subscribeToConfiantCommFrame(targetWindow) { + targetWindow.addEventListener('message', reportBillableEvents); +} + +let mutationObserver; +/** + * Set up mutation observer to subscribe to Confiant's communication channel ASAP + */ +function setUpMutationObserver() { + mutationObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + mutation.addedNodes.forEach((addedNode) => { + if (addedNode.nodeName === 'IFRAME' && addedNode.name === 'cnftComm' && !addedNode.pbjsModuleSubscribed) { + addedNode.pbjsModuleSubscribed = true; + mutationObserver.disconnect(); + mutationObserver = null; + const iframeWindow = addedNode.contentWindow; + subscribeToConfiantCommFrame(iframeWindow); + } + }); + }); + }); + mutationObserver.observe(document.head, { childList: true, subtree: true }); +} + +/** + * Emit billable event when Confiant integration reports that it has monitored an impression + */ +function reportBillableEvents (e) { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + auctionId: e.data.auctionId, + billingId: generateUUID(), + transactionId: e.data.transactionId, + type: 'impression', + vendor: 'confiant' + }); +} + +/** + * Confiant submodule registration + */ +function registerConfiantSubmodule() { + submodule('realTimeData', { + name: 'confiant', + init: (config) => { + try { + return setupPage(config); + } catch (err) { + logError(err.message); + if (mutationObserver) { + mutationObserver.disconnect(); + } + return false; + } + } + }); +} + +registerConfiantSubmodule(); + +export default { + injectConfigScript, + setupPage, + subscribeToConfiantCommFrame, + setUpMutationObserver, + reportBillableEvents, + registerConfiantSubmodule +}; diff --git a/modules/confiantRtdProvider.md b/modules/confiantRtdProvider.md new file mode 100644 index 00000000000..e92c0aabcba --- /dev/null +++ b/modules/confiantRtdProvider.md @@ -0,0 +1,45 @@ +# Overview + +``` +Module Name: Confiant Inc. Rtd provider +Module Type: Rtd Provider +Maintainer: +``` + +Confiant’s module provides comprehensive detection of security, quality, and privacy threats across your ad stack. +Confiant is the industry leader in real-time detecting and blocking of bad ads when it comes to protecting your users and brand reputation. + +To start using this module, please contact [Confiant](https://www.confiant.com/contact) to get an account and customer key. + + +# Integration + +1) Build Prebid bundle with Confiant module included: + + +``` +gulp build --modules=confiantRtdProvider,... +``` + +2) Include the resulting bundle on your page. + +# Configuration + +Configuration of Confiant module is plain simple: + +```javascript +pbjs.setConfig({ + realTimeData: { + dataProviders: [{ + name: 'confiant', + params: { + // so please get in touch with us so we could help you to set up the module with proper parameters + propertyId: '', // required, string param, obtained from Confiant Inc. + prebidExcludeBidders: '', // optional, comma separated list of bidders to exclude from Confiant's prebid.js integration + prebidNameSpace: '', // optional, string param, namespace for prebid.js integration + shouldEmitBillableEvent: false, // optional, boolean param, upon being set to true enables firing of the BillableEvent upon Confiant's impression scanning + } + }] + } +}); +``` diff --git a/src/adloader.js b/src/adloader.js index 64408683e9f..01a77971b93 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -20,7 +20,8 @@ const _approvedLoadExternalJSList = [ 'hadron', 'medianet', 'improvedigital', - 'aaxBlockmeter' + 'aaxBlockmeter', + 'confiant' ] /** diff --git a/test/spec/modules/confiantRtdProvider_spec.js b/test/spec/modules/confiantRtdProvider_spec.js new file mode 100644 index 00000000000..987aca2e020 --- /dev/null +++ b/test/spec/modules/confiantRtdProvider_spec.js @@ -0,0 +1,107 @@ +import * as utils from '../../../src/utils.js'; +import * as hook from '../../../src/hook.js' +import * as events from '../../../src/events.js'; +import CONSTANTS from '../../../src/constants.json'; + +import confiantModule from '../../../modules/confiantRtdProvider.js'; + +const { + injectConfigScript, + setupPage, + subscribeToConfiantCommFrame, + registerConfiantSubmodule +} = confiantModule; + +describe('Confiant RTD module', function () { + describe('setupPage()', function() { + it('should return false if propertId is not present in config', function() { + expect(setupPage({})).to.be.false; + expect(setupPage({ params: {} })).to.be.false; + expect(setupPage({ params: { propertyId: '' } })).to.be.false; + }); + + it('should return true if expected parameters are present', function() { + expect(setupPage({ params: { propertyId: 'clientId' } })).to.be.true; + }); + }); + + describe('Module initialization', function() { + let insertElementStub; + beforeEach(function() { + insertElementStub = sinon.stub(utils, 'insertElement'); + }); + afterEach(function() { + utils.insertElement.restore(); + }); + + it('should subscribe to rovided Window object', function () { + const mockWindow = { addEventListener: sinon.spy() }; + + subscribeToConfiantCommFrame(mockWindow); + + sinon.assert.calledOnce(mockWindow.addEventListener); + }); + + it('should fire BillableEvent as a result for message in comm window', function() { + let listenerCallback; + const mockWindow = { addEventListener: (a, cb) => (listenerCallback = cb) }; + let billableEventsCounter = 0; + + events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, (e) => { + if (e.vendor === 'confiant') { + billableEventsCounter++; + expect(e.type).to.equal('impression'); + expect(e.billingId).to.be.a('number'); + expect(e.auctionId).to.equal('auctionId'); + expect(e.transactionId).to.equal('transactionId'); + } + }); + + subscribeToConfiantCommFrame(mockWindow); + listenerCallback({ + data: { + auctionId: 'auctionId', + transactionId: 'transactionId' + } + }); + listenerCallback({ + data: { + auctionId: 'auctionId', + transactionId: 'transactionId' + } + }); + + expect(billableEventsCounter).to.equal(2); + }); + }); + + describe('Sumbodule execution', function() { + let submoduleStub; + let insertElementStub; + beforeEach(function () { + submoduleStub = sinon.stub(hook, 'submodule'); + insertElementStub = sinon.stub(utils, 'insertElement'); + }); + afterEach(function () { + utils.insertElement.restore(); + submoduleStub.restore(); + }); + + function initModule() { + registerConfiantSubmodule(); + + expect(submoduleStub.calledOnceWith('realTimeData')).to.equal(true); + + const registeredSubmoduleDefinition = submoduleStub.getCall(0).args[1]; + expect(registeredSubmoduleDefinition).to.be.an('object'); + expect(registeredSubmoduleDefinition).to.have.own.property('name', 'confiant'); + expect(registeredSubmoduleDefinition).to.have.own.property('init').that.is.a('function'); + + return registeredSubmoduleDefinition; + } + + it('should register Confiant submodule', function () { + initModule(); + }); + }); +}); From 10cf68607b0e6a646fb5c16b67defa8710811941 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 28 Dec 2022 17:11:36 +0000 Subject: [PATCH 553/569] Prebid 7.31.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b703493403..c2405970bde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.31.0-pre", + "version": "7.31.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index f96909cc6b4..52138286c36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.31.0-pre", + "version": "7.31.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From e13592697d17a110e6e94ecc6b2c907cb89eb45c Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 28 Dec 2022 17:11:36 +0000 Subject: [PATCH 554/569] Increment version to 7.32.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2405970bde..4bfd2cea1f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.31.0", + "version": "7.32.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 52138286c36..10da88d1c6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.31.0", + "version": "7.32.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From ff98601bdc868c98627ba023e2907f1611eec6c5 Mon Sep 17 00:00:00 2001 From: denys-berzoy-confiant <50574764+denys-berzoy-confiant@users.noreply.github.com> Date: Wed, 28 Dec 2022 23:38:46 +0300 Subject: [PATCH 555/569] Confiant RTD Provider: (#9382) - fix comment line --- modules/confiantRtdProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/confiantRtdProvider.js b/modules/confiantRtdProvider.js index 4a524b92d75..2c13ad87d75 100644 --- a/modules/confiantRtdProvider.js +++ b/modules/confiantRtdProvider.js @@ -4,7 +4,7 @@ * * The {@link module:modules/realTimeData} module is required * The module will inject a Confiant Inc. script into the page to monitor ad impressions - * @module modules/cleanioRtdProvider + * @module modules/confiantRtdProvider * @requires module:modules/realTimeData */ From 0caca74e57de50daf23854cbeccdd57ecac7a621 Mon Sep 17 00:00:00 2001 From: inna Date: Thu, 29 Dec 2022 15:39:33 +0200 Subject: [PATCH 556/569] Rise Bid Adapter: added isWrapper parameter to adapter request (#9329) * add Rise adapter * fixes * change param isOrg to org * Rise adapter * change email for rise * fix circle failed * bump * bump * bump * remove space * Upgrade Rise adapter to 5.0 * added isWrapper param * addes is_wrapper parameter to documentation * added is_wrapper to test * removed isWrapper Co-authored-by: Noam Tzuberi Co-authored-by: noamtzu Co-authored-by: Noam Tzuberi Co-authored-by: Laslo Chechur Co-authored-by: OronW <41260031+OronW@users.noreply.github.com> Co-authored-by: lasloche <62240785+lasloche@users.noreply.github.com> --- modules/riseBidAdapter.js | 3 ++- modules/riseBidAdapter.md | 2 ++ test/spec/modules/riseBidAdapter_spec.js | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 4325a699a3b..0c4d6148f2b 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -400,7 +400,8 @@ function generateGeneralParams(generalObject, bidderRequest) { dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, device_type: getDeviceType(navigator.userAgent), ua: navigator.userAgent, - session_id: getBidIdParameter('auctionId', generalObject), + is_wrapper: !!generalBidParams.isWrapper, + session_id: generalBidParams.sessionId || getBidIdParameter('auctionId', generalObject), tmax: timeout }; diff --git a/modules/riseBidAdapter.md b/modules/riseBidAdapter.md index 9dab3ec2a30..f0837cb5508 100644 --- a/modules/riseBidAdapter.md +++ b/modules/riseBidAdapter.md @@ -25,6 +25,8 @@ The adapter supports Video(instream). | `placementId` | optional | String | A unique placement identifier | "12345678" | `testMode` | optional | Boolean | This activates the test mode | false | `rtbDomain` | optional | String | Sets the seller end point | "www.test.com" +| `is_wrapper` | private | Boolean | Please don't use unless your account manager asked you to | false + # Test Parameters ```javascript diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index 37100073407..4fa4ff354ec 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -116,6 +116,13 @@ describe('riseAdapter', function () { expect(request.data.bids[0].placementId).to.equal(placementId); }); + it('sends the is_wrapper parameter to ENDPOINT via POST', function() { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.params).to.be.an('object'); + expect(request.data.params).to.have.property('is_wrapper'); + expect(request.data.params.is_wrapper).to.equal(false); + }); + it('sends bid request to ENDPOINT via POST', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.url).to.equal(ENDPOINT); From 0b1ba0cecf0d7dd796a0a661c0ecb8f582f55a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Thu, 29 Dec 2022 20:12:48 +0200 Subject: [PATCH 557/569] Added video media type support (#9326) --- modules/kueezRtbBidAdapter.js | 29 +++- test/spec/modules/kueezRtbBidAdapter_spec.js | 171 ++++++++++++++++--- 2 files changed, 168 insertions(+), 32 deletions(-) diff --git a/modules/kueezRtbBidAdapter.js b/modules/kueezRtbBidAdapter.js index 1ee3b7331cb..4b0e5055328 100644 --- a/modules/kueezRtbBidAdapter.js +++ b/modules/kueezRtbBidAdapter.js @@ -1,6 +1,6 @@ import { _each, deepAccess, parseSizesInput, parseUrl, uniques, isFn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; const GVLID = 1165; @@ -55,7 +55,7 @@ function isBidRequestValid(bid) { } function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { - const { params, bidId, userId, adUnitCode, schain } = bid; + const { params, bidId, userId, adUnitCode, schain, mediaTypes } = bid; let { bidFloor, ext } = params; const hashUrl = hashCode(topWindowUrl); const uniqueDealId = getUniqueDealId(hashUrl); @@ -89,7 +89,8 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { bidderVersion: BIDDER_VERSION, prebidVersion: '$prebid.version$', res: `${screen.width}x${screen.height}`, - schain: schain + schain: schain, + mediaTypes: mediaTypes }; appendUserIdsToRequestPayload(data, userId); @@ -167,11 +168,12 @@ function interpretResponse(serverResponse, request) { try { results.forEach(result => { - const { creativeId, ad, price, exp, width, height, currency, advertiserDomains } = result; + const { creativeId, ad, price, exp, width, height, currency, advertiserDomains, mediaType = BANNER } = result; if (!ad || !price) { return; } - output.push({ + + const response = { requestId: bidId, cpm: price, width: width, @@ -180,11 +182,22 @@ function interpretResponse(serverResponse, request) { currency: currency || CURRENCY, netRevenue: true, ttl: exp || TTL_SECONDS, - ad: ad, meta: { advertiserDomains: advertiserDomains || [] } - }) + }; + + if (mediaType === BANNER) { + Object.assign(response, { + ad: ad, + }); + } else { + Object.assign(response, { + vastXml: ad, + mediaType: VIDEO + }); + } + output.push(response); }); return output; } catch (e) { @@ -268,7 +281,7 @@ export const spec = { code: BIDDER_CODE, version: BIDDER_VERSION, gvlid: GVLID, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid, buildRequests, interpretResponse, diff --git a/test/spec/modules/kueezRtbBidAdapter_spec.js b/test/spec/modules/kueezRtbBidAdapter_spec.js index f9b2cd41a43..30b2a46cefb 100644 --- a/test/spec/modules/kueezRtbBidAdapter_spec.js +++ b/test/spec/modules/kueezRtbBidAdapter_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, SUPPORTED_ID_SYSTEMS, @@ -13,8 +13,9 @@ import { getUniqueDealId, } from 'modules/kueezRtbBidAdapter.js'; import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes'; const SUB_DOMAIN = 'exchange'; @@ -36,9 +37,42 @@ const BID = { 'sizes': [[300, 250], [300, 600]], 'bidderRequestId': '1fdb5ff1b6eaa7', 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', - 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc' + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'mediaTypes': [BANNER] }; +const VIDEO_BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', + 'bidderRequestId': '12a8ae9ada9c13', + 'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee', + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '635509f7ff6642d368cb9837', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1 + }, + 'sizes': [[545, 307]], + 'mediaTypes': { + 'video': { + 'playerSize': [[545, 307]], + 'context': 'instream', + 'mimes': [ + 'video/mp4', + 'application/javascript' + ], + 'protocols': [2, 3, 5, 6], + 'maxduration': 60, + 'minduration': 0, + 'startdelay': 0, + 'linearity': 1, + 'api': [2], + 'placement': 1 + } + } +} + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -73,6 +107,23 @@ const SERVER_RESPONSE = { } }; +const VIDEO_SERVER_RESPONSE = { + body: { + 'cid': '635509f7ff6642d368cb9837', + 'results': [{ + 'ad': '', + 'advertiserDomains': ['kueezrtb.com'], + 'exp': 60, + 'width': 545, + 'height': 307, + 'mediaType': 'video', + 'creativeId': '12610997325162499419', + 'price': 2, + 'cookies': [] + }] + } +}; + const REQUEST = { data: { width: 300, @@ -83,7 +134,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -111,6 +162,11 @@ describe('KueezRtbBidAdapter', function () { it('exists and is a string', function () { expect(adapter.code).to.exist.and.to.be.a('string'); }); + + it('exists and contains media types', function () { + expect(adapter.supportedMediaTypes).to.exist.and.to.be.an('array').with.length(2); + expect(adapter.supportedMediaTypes).to.contain.members([BANNER, VIDEO]); + }); }); describe('validate bid requests', function () { @@ -155,7 +211,53 @@ describe('KueezRtbBidAdapter', function () { sandbox.stub(Date, 'now').returns(1000); }); - it('should build request for each size', function () { + it('should build video request', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + const requests = adapter.buildRequests([VIDEO_BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/635509f7ff6642d368cb9837`, + data: { + adUnitCode: '63550ad1ff6642d368cba59dh5884270560', + bidFloor: 0.1, + bidId: '2d52001cabd527', + bidderVersion: adapter.version, + cb: 1000, + gdpr: 1, + gdprConsent: 'consent_string', + usPrivacy: 'consent_string', + prebidVersion: version, + publisherId: '59ac17c192832d0011283fe3', + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + res: `${window.top.screen.width}x${window.top.screen.height}`, + schain: VIDEO_BID.schain, + sizes: ['545x307'], + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + uqs: getTopWindowQueryParams(), + mediaTypes: { + video: { + api: [2], + context: 'instream', + linearity: 1, + maxduration: 60, + mimes: [ + 'video/mp4', + 'application/javascript' + ], + minduration: 0, + placement: 1, + playerSize: [[545, 307]], + protocols: [2, 3, 5, 6], + startdelay: 0 + } + } + } + }); + }); + + it('should build banner request for each size', function () { const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); const requests = adapter.buildRequests([BID], BIDDER_REQUEST); expect(requests).to.have.length(1); @@ -179,6 +281,7 @@ describe('KueezRtbBidAdapter', function () { prebidVersion: version, schain: BID.schain, res: `${window.top.screen.width}x${window.top.screen.height}`, + mediaTypes: [BANNER], uqs: getTopWindowQueryParams(), 'ext.param1': 'loremipsum', 'ext.param2': 'dolorsitamet', @@ -193,7 +296,7 @@ describe('KueezRtbBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -202,7 +305,7 @@ describe('KueezRtbBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.kueezrtb.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' @@ -210,7 +313,7 @@ describe('KueezRtbBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.kueezrtb.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', @@ -226,16 +329,16 @@ describe('KueezRtbBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); - it('should return an array of interpreted responses', function () { + it('should return an array of interpreted banner responses', function () { const responses = adapter.interpretResponse(SERVER_RESPONSE, REQUEST); expect(responses).to.have.length(1); expect(responses[0]).to.deep.equal({ @@ -254,6 +357,26 @@ describe('KueezRtbBidAdapter', function () { }); }); + it('should return an array of interpreted video responses', function () { + const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 2, + width: 545, + height: 307, + mediaType: 'video', + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 60, + vastXml: '', + meta: { + advertiserDomains: ['kueezrtb.com'] + } + }); + }); + it('should take default TTL', function () { const serverResponse = utils.deepClone(SERVER_RESPONSE); delete serverResponse.body.results[0].exp; @@ -271,11 +394,11 @@ describe('KueezRtbBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'parrableId': - return {eid: id}; + return { eid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -294,18 +417,18 @@ describe('KueezRtbBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -365,7 +488,7 @@ describe('KueezRtbBidAdapter', function () { now }); setStorageItem('myKey', 2020); - const {value, created} = getStorageItem('myKey'); + const { value, created } = getStorageItem('myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -381,8 +504,8 @@ describe('KueezRtbBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); From 57555bc4a0b916bd32dbcdf5030d3ead20a103d1 Mon Sep 17 00:00:00 2001 From: "Adserver.Online" <61009237+adserver-online@users.noreply.github.com> Date: Tue, 3 Jan 2023 17:22:48 +0200 Subject: [PATCH 558/569] Aso Bid Adapter: add bcmint alias (#9387) * Add bcmint alias * kick off tests Co-authored-by: dev Co-authored-by: Chris Huie --- modules/asoBidAdapter.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/asoBidAdapter.js b/modules/asoBidAdapter.js index 83b182e4ca5..39c42f193b2 100644 --- a/modules/asoBidAdapter.js +++ b/modules/asoBidAdapter.js @@ -10,11 +10,11 @@ import { parseSizesInput, tryAppendQueryString } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; -import {parseDomain} from '../src/refererDetection.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { parseDomain } from '../src/refererDetection.js'; const BIDDER_CODE = 'aso'; const DEFAULT_SERVER_URL = 'https://srv.aso1.net'; @@ -27,6 +27,9 @@ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], + aliases: [ + {code: 'bcmint'} + ], isBidRequestValid: bid => { return !!bid.params && !!bid.params.zone; From ccc9bba348275e941d39ea32284d4983cbcc20d2 Mon Sep 17 00:00:00 2001 From: Giovanni Sollazzo Date: Tue, 3 Jan 2023 17:02:37 +0100 Subject: [PATCH 559/569] AIDEM Bid Adapter: added wpar and placementId param (#9377) * AIDEM Bid Adapter * Added _spec.js * update * Fix Navigator in _spec.js * Removed timeout handler. * Added publisherId as required bidder params * moved publisherId into site publisher object * Added wpar to environment * Added placementId parameter * added unit tests for the wpar environment object Co-authored-by: darkstar Co-authored-by: AndreaC <67786179+darkstarac@users.noreply.github.com> --- modules/aidemBidAdapter.js | 4 ++++ modules/aidemBidAdapter.md | 1 + test/spec/modules/aidemBidAdapter_spec.js | 12 ++++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/aidemBidAdapter.js b/modules/aidemBidAdapter.js index 41bf680db1c..081a0324ddb 100644 --- a/modules/aidemBidAdapter.js +++ b/modules/aidemBidAdapter.js @@ -220,6 +220,8 @@ function setPrebidImpressionObject(bidRequests, payload) { deepSetValue(impressionObject, 'id', bidRequest.bidId); // Transaction id deepSetValue(impressionObject, 'tid', deepAccess(bidRequest, 'transactionId')); + // Placement id + deepSetValue(impressionObject, 'tagid', deepAccess(bidRequest, 'params.placementId', null)); // Publisher id deepSetValue(payload, 'site.publisher.id', deepAccess(bidRequest, 'params.publisherId')); // Site id @@ -265,6 +267,8 @@ function setPrebidRequestEnvironment(payload) { deepSetValue(payload, 'environment.inp.jp', window.JSON.parse.name === 'parse' && typeof window.JSON.parse.prototype === 'undefined'); deepSetValue(payload, 'environment.inp.ofe', window.Object.fromEntries.name === 'fromEntries' && typeof window.Object.fromEntries.prototype === 'undefined'); deepSetValue(payload, 'environment.inp.oa', window.Object.assign.name === 'assign' && typeof window.Object.assign.prototype === 'undefined'); + deepSetValue(payload, 'environment.wpar.innerWidth', window.innerWidth); + deepSetValue(payload, 'environment.wpar.innerHeight', window.innerHeight); } function setPrebidImpressionObjectFloor(bidRequest, impressionObject) { diff --git a/modules/aidemBidAdapter.md b/modules/aidemBidAdapter.md index a5beca97d10..342a264da01 100644 --- a/modules/aidemBidAdapter.md +++ b/modules/aidemBidAdapter.md @@ -18,6 +18,7 @@ This module is GDPR and CCPA compliant, and no 3rd party userIds are allowed. |---------------|----------|---------------------|---------------|----------| | `siteId` | required | Unique site ID | `'ABCDEF'` | `String` | | `publisherId` | required | Unique publisher ID | `'ABCDEF'` | `String` | +| `placementId` | optional | Unique publisher tag ID | `'ABCDEF'` | `String` | ### Banner Bid Params diff --git a/test/spec/modules/aidemBidAdapter_spec.js b/test/spec/modules/aidemBidAdapter_spec.js index 472b226ab8a..71edfcf82fb 100644 --- a/test/spec/modules/aidemBidAdapter_spec.js +++ b/test/spec/modules/aidemBidAdapter_spec.js @@ -385,7 +385,7 @@ describe('Aidem adapter', () => { expect(payload.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_BANNER_REQUESTS.length) expect(payload.imp[0]).to.be.a('object').that.has.all.keys( - 'banner', 'id', 'mediatype', 'imp_ext', 'tid' + 'banner', 'id', 'mediatype', 'imp_ext', 'tid', 'tagid' ) expect(payload.imp[0].banner).to.be.a('object').that.has.all.keys( 'format', 'topframe' @@ -401,7 +401,7 @@ describe('Aidem adapter', () => { expect(payload.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_VIDEO_REQUESTS.length) expect(payload.imp[0]).to.be.a('object').that.has.all.keys( - 'video', 'id', 'mediatype', 'imp_ext', 'tid' + 'video', 'id', 'mediatype', 'imp_ext', 'tid', 'tagid' ) expect(payload.imp[0].video).to.be.a('object').that.has.all.keys( 'format', 'mimes', 'minDuration', 'maxDuration', 'protocols' @@ -421,6 +421,14 @@ describe('Aidem adapter', () => { 'value', 'currency' ) }); + + it('should hav wpar keys in environment object', function () { + const requests = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST); + const payload = JSON.parse(requests.data) + expect(payload).to.have.property('environment') + expect(payload.environment).to.be.a('object').that.have.property('wpar') + expect(payload.environment.wpar).to.be.a('object').that.has.keys('innerWidth', 'innerHeight') + }); }) describe('interpretResponse', () => { From a2172d9aa3b56e8992460e796760843cb9fa947c Mon Sep 17 00:00:00 2001 From: ahmadlob <109217988+ahmadlob@users.noreply.github.com> Date: Tue, 3 Jan 2023 18:54:25 +0200 Subject: [PATCH 560/569] Taboola Bid Adapter: onBidWon, userSyncs, gpp support and FPD (#9376) * on-bid-won * support-fpd * support-fpd * support-fpd * support-fpd * support-fpd * support-fpd * implement-get-user-sync * implement-get-user-sync * implement-get-user-sync * implement-get-user-sync * implement-get-user-sync * implement-get-user-sync * implement-get-user-sync * implement-get-user-sync * implement-get-user-sync * position-pagetype --- modules/taboolaBidAdapter.js | 62 ++++++++-- test/spec/modules/taboolaBidAdapter_spec.js | 124 +++++++++++++++++++- 2 files changed, 172 insertions(+), 14 deletions(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 670d28ab64e..85adfc31ca5 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -3,13 +3,15 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; -import {getWindowSelf} from '../src/utils.js' +import {getWindowSelf, replaceAuctionPrice} from '../src/utils.js' import {getStorageManager} from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'taboola'; const GVLID = 42; const CURRENCY = 'USD'; export const END_POINT_URL = 'https://display.bidder.taboola.com/OpenRTB/TaboolaHB/auction'; +export const USER_SYNC_IMG_URL = 'https://trc.taboola.com/sg/prebidJS/1/cm'; const USER_ID = 'user-id'; const STORAGE_KEY = `taboola global:${USER_ID}`; const COOKIE_KEY = 'trc_cookie_storage'; @@ -81,7 +83,7 @@ export const spec = { const [bidRequest] = validBidRequests; const {refererInfo, gdprConsent = {}, uspConsent} = bidderRequest; const {publisherId} = bidRequest.params; - const site = getSiteProperties(bidRequest.params, refererInfo); + const site = getSiteProperties(bidRequest.params, refererInfo, bidderRequest.ortb2); const device = {ua: navigator.userAgent}; const imps = getImps(validBidRequests); const user = { @@ -102,24 +104,32 @@ export const spec = { regs.ext.us_privacy = uspConsent; } + if (bidderRequest.ortb2?.regs?.gpp) { + regs.ext.gpp = bidderRequest.ortb2.regs.gpp; + regs.ext.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; + } + if (config.getConfig('coppa')) { regs.coppa = 1; } const ortb2 = bidderRequest.ortb2 || { + bcat: [], badv: [], - bcat: [] + wlang: [] }; const request = { id: bidderRequest.auctionId, imp: imps, site, + pageType: ortb2?.ext?.data?.pageType || ortb2?.ext?.data?.section || bidRequest.params.pageType, device, source: {fd: 1}, tmax: bidderRequest.timeout, - bcat: ortb2.bcat, - badv: ortb2.badv, + bcat: ortb2.bcat || bidRequest.params.bcat || [], + badv: ortb2.badv || bidRequest.params.badv || [], + wlang: ortb2.wlang || bidRequest.params.wlang || [], user, regs }; @@ -149,16 +159,45 @@ export const spec = { return bidResponses.map((bidResponse) => getBid(bids, currency, bidResponse)).filter(Boolean); }, + onBidWon: (bid) => { + if (bid.nurl) { + const resolvedNurl = replaceAuctionPrice(bid.nurl, bid.originalCpm); + ajax(resolvedNurl); + } + }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + const syncs = [] + const queryParams = []; + if (gdprConsent) { + queryParams.push(`gdpr=${Number(gdprConsent.gdprApplies && 1)}&gdpr_consent=${encodeURIComponent(gdprConsent.consentString || '')}`); + } + + if (uspConsent) { + queryParams.push('us_privacy=' + encodeURIComponent(uspConsent)); + } + + if (gppConsent) { + queryParams.push('gpp=' + encodeURIComponent(gppConsent)); + } + + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: USER_SYNC_IMG_URL + (queryParams.length ? '?' + queryParams.join('&') : '') + }); + } + return syncs; + }, }; -function getSiteProperties({publisherId, bcat = []}, refererInfo) { +function getSiteProperties({publisherId}, refererInfo, ortb2) { const {getPageUrl, getReferrer} = internal; return { id: publisherId, name: publisherId, - domain: refererInfo?.domain || window.location?.host, - page: getPageUrl(refererInfo), - ref: getReferrer(refererInfo), + domain: ortb2?.site?.domain || refererInfo?.domain || window.location?.host, + page: ortb2?.site?.page || getPageUrl(refererInfo), + ref: ortb2?.site?.ref || getReferrer(refererInfo), publisher: { id: publisherId }, @@ -170,11 +209,12 @@ function getSiteProperties({publisherId, bcat = []}, refererInfo) { function getImps(validBidRequests) { return validBidRequests.map((bid, id) => { - const {tagId} = bid.params; + const {tagId, position} = bid.params; const imp = { id: id + 1, banner: getBanners(bid), - tagid: tagId + tagid: tagId, + position: position } if (typeof bid.getFloor === 'function') { const floorInfo = bid.getFloor({ diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 5bde75cd0b6..a3a765d28cf 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -2,6 +2,7 @@ import {expect} from 'chai'; import {spec, internal, END_POINT_URL, userData} from 'modules/taboolaBidAdapter.js'; import {config} from '../../../src/config' import * as utils from '../../../src/utils' +import {server} from '../../mocks/xhr' describe('Taboola Adapter', function () { let hasLocalStorage, cookiesAreEnabled, getDataFromLocalStorage, localStorageIsEnabled, getCookie, commonBidRequest; @@ -91,6 +92,31 @@ describe('Taboola Adapter', function () { }) }) + describe('onBidWon', function () { + it('onBidWon exist as a function', () => { + expect(spec.onBidWon).to.exist.and.to.be.a('function'); + }); + + it('should resolve price macro in nurl', function () { + const nurl = 'http://win.example.com/${AUCTION_PRICE}'; + const bid = { + requestId: 1, + cpm: 2, + originalCpm: 3.4, + creativeId: 1, + ttl: 60, + netRevenue: true, + mediaType: 'banner', + ad: '...', + width: 300, + height: 250, + nurl: nurl + } + spec.onBidWon(bid); + expect(server.requests[0].url).to.equals('http://win.example.com/3.4') + }); + }); + describe('buildRequests', function () { const defaultBidRequest = { ...createBidRequest(), @@ -137,6 +163,7 @@ describe('Taboola Adapter', function () { 'source': {'fd': 1}, 'bcat': [], 'badv': [], + 'wlang': [], 'user': { 'buyeruid': 0, 'ext': {}, @@ -184,7 +211,7 @@ describe('Taboola Adapter', function () { expect(resData.imp[0].bidfloorcur).to.deep.equal('USD'); }); - it('should pass bid floor even if they is a bid floor param', function () { + it('should pass bid floor even if it is a bid floor param', function () { const optionalParams = { bidfloor: 0.25, bidfloorcur: 'EUR' @@ -206,6 +233,21 @@ describe('Taboola Adapter', function () { expect(resData.imp[0].bidfloorcur).to.deep.equal('USD'); }); + it('should pass impression position', function () { + const optionalParams = { + position: 2 + }; + + const bidRequest = { + ...defaultBidRequest, + params: {...commonBidRequest.params, ...optionalParams} + }; + + const res = spec.buildRequests([bidRequest], commonBidderRequest); + const resData = JSON.parse(res.data); + expect(resData.imp[0].position).to.deep.equal(2); + }); + it('should pass bidder timeout', function () { const bidderRequest = { ...commonBidderRequest, @@ -216,6 +258,40 @@ describe('Taboola Adapter', function () { expect(resData.tmax).to.equal(500); }); + describe('first party data', function () { + it('should parse first party data', function () { + const bidderRequest = { + ...commonBidderRequest, + ortb2: { + bcat: ['EX1', 'EX2', 'EX3'], + badv: ['site.com'], + wlang: ['de'], + } + } + const res = spec.buildRequests([defaultBidRequest], bidderRequest); + const resData = JSON.parse(res.data); + expect(resData.bcat).to.deep.equal(bidderRequest.ortb2.bcat) + expect(resData.badv).to.deep.equal(bidderRequest.ortb2.badv) + expect(resData.wlang).to.deep.equal(bidderRequest.ortb2.wlang) + }); + + it('should pass pageType if exists in ortb2', function () { + const bidderRequest = { + ...commonBidderRequest, + ortb2: { + ext: { + data: { + pageType: 'news' + } + } + } + } + const res = spec.buildRequests([defaultBidRequest], bidderRequest); + const resData = JSON.parse(res.data); + expect(resData.pageType).to.deep.equal(bidderRequest.ortb2.ext.data.pageType); + }); + }); + describe('handle privacy segments when building request', function () { it('should pass GDPR consent', function () { const bidderRequest = { @@ -234,6 +310,20 @@ describe('Taboola Adapter', function () { expect(resData.regs.ext.gdpr).to.equal(1) }); + it('should pass GPP consent if exist in ortb2', function () { + const ortb2 = { + regs: { + gpp: 'testGpp', + gpp_sid: [1, 2, 3] + } + } + + const res = spec.buildRequests([defaultBidRequest], {...commonBidderRequest, ortb2}) + const resData = JSON.parse(res.data) + expect(resData.regs.ext.gpp).to.equal('testGpp') + expect(resData.regs.ext.gpp_sid).to.deep.equal([1, 2, 3]) + }); + it('should pass us privacy consent', function () { const bidderRequest = { refererInfo: { @@ -552,8 +642,36 @@ describe('Taboola Adapter', function () { }); }) - describe('userData', function () { - // todo: add UT for getUserSyncs + describe('getUserSyncs', function () { + const usersyncUrl = 'https://trc.taboola.com/sg/prebidJS/1/cm'; + + it('should not return user sync if pixelEnabled is false', function () { + const res = spec.getUserSyncs({pixelEnabled: false}); + expect(res).to.be.an('array').that.is.empty; + }); + + it('should return user sync if pixelEnabled is true', function () { + const res = spec.getUserSyncs({pixelEnabled: true}); + expect(res).to.deep.equal([{type: 'image', url: usersyncUrl}]); + }); + + it('should pass consent tokens values', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'GDPR_CONSENT'}, 'USP_CONSENT')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=GDPR_CONSENT&us_privacy=USP_CONSENT` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: undefined}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?gdpr=0&gdpr_consent=` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: undefined}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?gdpr=0&gdpr_consent=` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, 'USP_CONSENT')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?us_privacy=USP_CONSENT` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, 'USP_CONSENT', 'GPP_STRING')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?us_privacy=USP_CONSENT&gpp=GPP_STRING` + }]); + }); }) describe('internal functions', function () { From 348ba1da17739175f64c02120ab99cfe853aa49d Mon Sep 17 00:00:00 2001 From: nkloeber <100145701+nkloeber@users.noreply.github.com> Date: Tue, 3 Jan 2023 18:11:12 +0100 Subject: [PATCH 561/569] Yieldlab Bid Adapter: read and pass UserIdsAsEids atype information (#9370) * YieldlabBidAdapter read atype information from UserIdsAsEids and pass it as query parameter (atypes={idprovider}:{atype},{idprovider2}:{atype2},...) * Update type hint and add semi colons Co-authored-by: Christoph Kipping <29540638+kippsterr@users.noreply.github.com> --- modules/yieldlabBidAdapter.js | 16 ++++++++++++++++ test/spec/modules/yieldlabBidAdapter_spec.js | 12 +++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 88e1494d416..6e4f6644140 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -62,6 +62,7 @@ export const spec = { } if (bid.userIdAsEids && Array.isArray(bid.userIdAsEids)) { query.ids = createUserIdString(bid.userIdAsEids) + query.atypes = createUserIdAtypesString(bid.userIdAsEids) } if (bid.params.customParams && isPlainObject(bid.params.customParams)) { for (const prop in bid.params.customParams) { @@ -303,6 +304,21 @@ function createUserIdString(eids) { return str.join(',') } +/** + * Creates a string from an array of eids with ID provider and atype if atype exists + * @param {Array.<{source: String, uids: Array.<{id: String, atype: Number, ext: Object}>}>} eids + * @returns {String} idprovider:atype,idprovider2:atype2,... + */ +function createUserIdAtypesString(eids) { + const str = []; + for (let i = 0; i < eids.length; i++) { + if (eids[i].uids[0].atype) { + str.push(eids[i].source + ':' + eids[i].uids[0].atype); + } + } + return str.join(','); +} + /** * Creates a querystring out of an object with key-values * @param {Object} obj diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 5df0e93d34e..07d42df1319 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -45,6 +45,12 @@ const DEFAULT_REQUEST = () => ({ id: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', atype: 1 }] + }, { + source: 'digitrust.de', + uids: [{ + id: 'd8aa10fa-d86c-451d-aad8-5f16162a9e64', + atype: 2 + }] }], schain: { ver: '1.0', @@ -271,7 +277,11 @@ describe('yieldlabBidAdapter', () => { }) it('passes userids to bid request', () => { - expect(request.url).to.include('ids=netid.de%3AfH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg') + expect(request.url).to.include('ids=netid.de%3AfH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg%2Cdigitrust.de%3Ad8aa10fa-d86c-451d-aad8-5f16162a9e64') + }) + + it('passes atype to bid request', () => { + expect(request.url).to.include('atypes=netid.de%3A1%2Cdigitrust.de%3A2') }) it('passes extra params to bid request', () => { From 1960ed811bd923a445aece06b20f06e11589bf1d Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 3 Jan 2023 12:54:41 -0700 Subject: [PATCH 562/569] Medianet RTD module: fix `getTargetingData` to retrieve correct adUnits (#9392) --- modules/medianetRtdProvider.js | 8 ++++---- test/spec/modules/medianetRtdProvider_spec.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/medianetRtdProvider.js b/modules/medianetRtdProvider.js index 3e01d0e5631..5a159b39081 100644 --- a/modules/medianetRtdProvider.js +++ b/modules/medianetRtdProvider.js @@ -59,14 +59,14 @@ function onAuctionInitEvent(auctionInit) { }, SOURCE)); } -function getTargetingData(adUnitCode) { - const adUnits = getAdUnits(undefined, adUnitCode); +function getTargetingData(adUnitCodes, config, consent, auction) { + const adUnits = getAdUnits(auction.adUnits, adUnitCodes); let targetingData = {}; if (window.mnjs.loaded && isFn(window.mnjs.getTargetingData)) { - targetingData = window.mnjs.getTargetingData(adUnitCode, adUnits, SOURCE) || {}; + targetingData = window.mnjs.getTargetingData(adUnitCodes, adUnits, SOURCE) || {}; } const targeting = {}; - adUnitCode.forEach(adUnitCode => { + adUnitCodes.forEach(adUnitCode => { targeting[adUnitCode] = targeting[adUnitCode] || {}; targetingData[adUnitCode] = targetingData[adUnitCode] || {}; targeting[adUnitCode] = { diff --git a/test/spec/modules/medianetRtdProvider_spec.js b/test/spec/modules/medianetRtdProvider_spec.js index 7d73ecd5d44..f9d4ef7c2cf 100644 --- a/test/spec/modules/medianetRtdProvider_spec.js +++ b/test/spec/modules/medianetRtdProvider_spec.js @@ -66,12 +66,12 @@ describe('medianet realtime module', function () { describe('getTargeting should work correctly', function () { it('should return empty if not loaded', function () { window.mnjs.loaded = false; - assert.deepEqual(medianetRTD.medianetRtdModule.getTargetingData([]), {}); + assert.deepEqual(medianetRTD.medianetRtdModule.getTargetingData([], {}, {}, {}), {}); }); it('should return ad unit codes when ad units are present', function () { const adUnitCodes = ['code1', 'code2']; - assert.deepEqual(medianetRTD.medianetRtdModule.getTargetingData(adUnitCodes), { + assert.deepEqual(medianetRTD.medianetRtdModule.getTargetingData(adUnitCodes, {}, {}, {}), { code1: {'mnadc': 'code1'}, code2: {'mnadc': 'code2'}, }); @@ -79,7 +79,7 @@ describe('medianet realtime module', function () { it('should call mnjs.getTargetingData if loaded', function () { window.mnjs.loaded = true; - medianetRTD.medianetRtdModule.getTargetingData([]); + medianetRTD.medianetRtdModule.getTargetingData([], {}, {}, {}); assert.equal(getTargetingDataSpy.called, true); }); }); From febd71b40884ebc23dfb448f8a7096168eaffdcb Mon Sep 17 00:00:00 2001 From: Krzysztof Desput Date: Wed, 4 Jan 2023 11:20:42 +0100 Subject: [PATCH 563/569] Holid Bid Adapter: initial release (#9371) * Holid bid adapter * Adjust test to various device sizes * Include first party data from ortb2 object * Remove trailing spaces in test --- modules/holidBidAdapter.js | 170 ++++++++++++++++++++++ modules/holidBidAdapter.md | 36 +++++ test/spec/modules/holidBidAdapter_spec.js | 165 +++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 modules/holidBidAdapter.js create mode 100644 modules/holidBidAdapter.md create mode 100644 test/spec/modules/holidBidAdapter_spec.js diff --git a/modules/holidBidAdapter.js b/modules/holidBidAdapter.js new file mode 100644 index 00000000000..be5a7a044c3 --- /dev/null +++ b/modules/holidBidAdapter.js @@ -0,0 +1,170 @@ +import { + deepAccess, + getBidIdParameter, + isStr, + logMessage, + triggerPixel, +} from '../src/utils.js' +import * as events from '../src/events.js' +import CONSTANTS from '../src/constants.json' +import { BANNER } from '../src/mediaTypes.js' + +import { registerBidder } from '../src/adapters/bidderFactory.js' + +const BIDDER_CODE = 'holid' +const GVLID = 1177 +const ENDPOINT = 'https://helloworld.holid.io/openrtb2/auction' +const COOKIE_SYNC_ENDPOINT = 'https://null.holid.io/sync.html' +const TIME_TO_LIVE = 300 +let wurlMap = {} + +events.on(CONSTANTS.EVENTS.BID_WON, bidWonHandler) + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function (bid) { + return !!bid.params.adUnitID + }, + + buildRequests: function (validBidRequests, _bidderRequest) { + return validBidRequests.map((bid) => { + const requestData = { + ...bid.ortb2, + id: bid.auctionId, + imp: [getImp(bid)], + } + + return { + method: 'POST', + url: ENDPOINT, + data: JSON.stringify(requestData), + bidId: bid.bidId, + } + }) + }, + + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = [] + + if (!serverResponse.body.seatbid) { + return [] + } + + serverResponse.body.seatbid.map((response) => { + response.bid.map((bid) => { + const requestId = bidRequest.bidId + const auctionId = bidRequest.auctionId + const wurl = deepAccess(bid, 'ext.prebid.events.win') + const bidResponse = { + requestId, + cpm: bid.price, + width: bid.w, + height: bid.h, + ad: bid.adm, + creativeId: bid.crid, + currency: serverResponse.body.cur, + netRevenue: true, + ttl: TIME_TO_LIVE, + } + + addWurl({ auctionId, requestId, wurl }) + + bidResponses.push(bidResponse) + }) + }) + + return bidResponses + }, + + getUserSyncs(optionsType, serverResponse, gdprConsent, uspConsent) { + if (!serverResponse || serverResponse.length === 0) { + return [] + } + + const syncs = [] + + if (optionsType.iframeEnabled) { + const queryParams = [] + + queryParams.push('bidders=' + getBidders(serverResponse)) + queryParams.push('gdpr=' + +gdprConsent.gdprApplies) + queryParams.push('gdpr_consent=' + gdprConsent.consentString) + queryParams.push('usp_consent=' + (uspConsent || '')) + + let strQueryParams = queryParams.join('&') + + if (strQueryParams.length > 0) { + strQueryParams = '?' + strQueryParams + } + + syncs.push({ + type: 'iframe', + url: COOKIE_SYNC_ENDPOINT + strQueryParams + '&type=iframe', + }) + + return syncs + } + }, +} + +function getImp(bid) { + const imp = { + ext: { + prebid: { + storedrequest: { + id: getBidIdParameter('adUnitID', bid.params), + }, + }, + }, + } + const sizes = + bid.sizes && !Array.isArray(bid.sizes[0]) ? [bid.sizes] : bid.sizes + + if (deepAccess(bid, 'mediaTypes.banner')) { + imp.banner = { + format: sizes.map((size) => { + return { w: size[0], h: size[1] } + }), + } + } + + return imp +} + +function getBidders(serverResponse) { + const bidders = serverResponse + .map((res) => Object.keys(res.body.ext.responsetimemillis)) + .flat(1) + + return encodeURIComponent(JSON.stringify([...new Set(bidders)])) +} + +function addWurl(auctionId, adId, wurl) { + if ([auctionId, adId].every(isStr)) { + wurlMap[`${auctionId}${adId}`] = wurl + } +} + +function removeWurl(auctionId, adId) { + delete wurlMap[`${auctionId}${adId}`] +} + +function getWurl(auctionId, adId) { + if ([auctionId, adId].every(isStr)) { + return wurlMap[`${auctionId}${adId}`] + } +} + +function bidWonHandler(bid) { + const wurl = getWurl(bid.auctionId, bid.adId) + if (wurl) { + logMessage(`Invoking image pixel for wurl on BID_WIN: "${wurl}"`) + triggerPixel(wurl) + removeWurl(bid.auctionId, bid.adId) + } +} + +registerBidder(spec) diff --git a/modules/holidBidAdapter.md b/modules/holidBidAdapter.md new file mode 100644 index 00000000000..1d83918c00a --- /dev/null +++ b/modules/holidBidAdapter.md @@ -0,0 +1,36 @@ +# Overview + +``` +Module Name: Holid Bid Adapter +Module Type: Bidder Adapter +Maintainer: richard@holid.se +``` + +# Description + +Currently module supports only banner mediaType. + +# Test Parameters + +## Sample Banner Ad Unit + +```js +var adUnits = [ + { + code: 'bannerAdUnit', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'holid', + params: { + adUnitID: '12345', + }, + }, + ], + }, +] +``` diff --git a/test/spec/modules/holidBidAdapter_spec.js b/test/spec/modules/holidBidAdapter_spec.js new file mode 100644 index 00000000000..e18a5ac58f4 --- /dev/null +++ b/test/spec/modules/holidBidAdapter_spec.js @@ -0,0 +1,165 @@ +import { expect } from 'chai' +import { spec } from 'modules/holidBidAdapter.js' + +describe('holidBidAdapterTests', () => { + const bidRequestData = { + bidder: 'holid', + adUnitCode: 'test-div', + bidId: 'bid-id', + auctionId: 'test-id', + params: { adUnitID: '12345' }, + mediaTypes: { banner: {} }, + sizes: [[300, 250]], + ortb2: { + site: { + publisher: { + domain: 'https://foo.bar', + } + }, + regs: { + gdpr: 1, + }, + user: { + ext: { + consent: 'G4ll0p1ng_Un1c0rn5', + } + }, + device: { + h: 410, + w: 1860, + } + } + } + + describe('isBidRequestValid', () => { + const bid = JSON.parse(JSON.stringify(bidRequestData)) + + it('should return true', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + + it('should return false when required params are not passed', () => { + const bid = JSON.parse(JSON.stringify(bidRequestData)) + delete bid.params.adUnitID + + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }) + + describe('buildRequests', () => { + const bid = JSON.parse(JSON.stringify(bidRequestData)) + const request = spec.buildRequests([bid], bid) + const payload = JSON.parse(request[0].data) + + it('should include ext in imp', () => { + expect(payload.imp[0].ext).to.exist + expect(payload.imp[0].ext).to.deep.equal({ + prebid: { storedrequest: { id: '12345' } }, + }) + }) + + it('should include banner format in imp', () => { + expect(payload.imp[0].banner).to.exist + expect(payload.imp[0].banner).to.deep.equal({ + format: [{ w: 300, h: 250 }], + }) + }) + + it('should include ortb2 first party data', () => { + expect(payload.device.w).to.equal(1860) + expect(payload.device.h).to.equal(410) + expect(payload.user.ext.consent).to.equal('G4ll0p1ng_Un1c0rn5') + expect(payload.regs.gdpr).to.equal(1) + }) + }) + + describe('interpretResponse', () => { + const serverResponse = { + body: { + id: 'test-id', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: 'testbidid', + price: 0.4, + adm: 'test-ad', + adid: 789456, + crid: 1234, + w: 300, + h: 250, + }, + ], + }, + ], + }, + } + + const interpretedResponse = spec.interpretResponse( + serverResponse, + bidRequestData + ) + + it('should interpret response', () => { + expect(interpretedResponse[0].requestId).to.equal(bidRequestData.bidId) + expect(interpretedResponse[0].cpm).to.equal( + serverResponse.body.seatbid[0].bid[0].price + ) + expect(interpretedResponse[0].ad).to.equal( + serverResponse.body.seatbid[0].bid[0].adm + ) + expect(interpretedResponse[0].creativeId).to.equal( + serverResponse.body.seatbid[0].bid[0].crid + ) + expect(interpretedResponse[0].width).to.equal( + serverResponse.body.seatbid[0].bid[0].w + ) + expect(interpretedResponse[0].height).to.equal( + serverResponse.body.seatbid[0].bid[0].h + ) + expect(interpretedResponse[0].currency).to.equal(serverResponse.body.cur) + }) + }) + + describe('getUserSyncs', () => { + it('should return user sync', () => { + const optionsType = { + iframeEnabled: true, + pixelEnabled: true, + } + const serverResponse = [ + { + body: { + ext: { + responsetimemillis: { + 'test seat 1': 2, + 'test seat 2': 1, + }, + }, + }, + }, + ] + const gdprConsent = { + gdprApplies: 1, + consentString: 'dkj49Sjmfjuj34as:12jaf90123hufabidfy9u23brfpoig', + } + const uspConsent = 'mkjvbiniwot4827obfoy8sdg8203gb' + const expectedUserSyncs = [ + { + type: 'iframe', + url: 'https://null.holid.io/sync.html?bidders=%5B%22test%20seat%201%22%2C%22test%20seat%202%22%5D&gdpr=1&gdpr_consent=dkj49Sjmfjuj34as:12jaf90123hufabidfy9u23brfpoig&usp_consent=mkjvbiniwot4827obfoy8sdg8203gb&type=iframe', + }, + ] + + const userSyncs = spec.getUserSyncs( + optionsType, + serverResponse, + gdprConsent, + uspConsent + ) + + expect(userSyncs).to.deep.equal(expectedUserSyncs) + }) + }) +}) From 1238d30cc986d3ac9c378329dc4e7f4bb37a9da7 Mon Sep 17 00:00:00 2001 From: Torsten Date: Thu, 5 Jan 2023 12:16:41 +0100 Subject: [PATCH 564/569] Appnexus Bid Adapter : add video data from the request to the bid response (#9396) * Appnexus adapter: add video data from the request to the bid response * kick off tests * remove change Co-authored-by: Chris Huie --- modules/appnexusBidAdapter.js | 1 + test/spec/modules/appnexusBidAdapter_spec.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 919831b8515..7465ab15780 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -731,6 +731,7 @@ function newBid(serverBid, rtbBid, bidderRequest) { displayUrl: nativeAd.displayurl, clickTrackers: nativeAd.link.click_trackers, impressionTrackers: nativeAd.impression_trackers, + video: nativeAd.video, javascriptTrackers: jsTrackers }; if (nativeAd.main_img) { diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 1ab8feceaeb..bae2417c278 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -1630,7 +1630,10 @@ describe('AppNexusAdapter', function () { 'phone': '1234567890', 'address': '28 W 23rd St, New York, NY 10010', 'privacy_link': 'https://appnexus.com/?url=privacy_url', - 'javascriptTrackers': '' + 'javascriptTrackers': '', + 'video': { + 'content': '' + } }; let bidderRequest = { bids: [{ @@ -1644,6 +1647,7 @@ describe('AppNexusAdapter', function () { expect(result[0].native.body).to.equal('Cool description great stuff'); expect(result[0].native.cta).to.equal('Do it'); expect(result[0].native.image.url).to.equal('https://cdn.adnxs.com/img.png'); + expect(result[0].native.video.content).to.equal(''); }); } From 978926d8e318985c04d5c5cc79851a32efb5ffc7 Mon Sep 17 00:00:00 2001 From: Olivier Date: Thu, 5 Jan 2023 17:22:43 +0100 Subject: [PATCH 565/569] AdagioBidAdapter: Remove some params (#9398) --- modules/adagioBidAdapter.js | 4 --- modules/adagioBidAdapter.md | 26 +++---------------- test/spec/modules/adagioBidAdapter_spec.js | 29 +--------------------- 3 files changed, 5 insertions(+), 54 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 4f86a914982..12fd50c6636 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -669,10 +669,8 @@ function autoFillParams(bid) { } // extra params - setExtraParam(bid, 'environment'); setExtraParam(bid, 'pagetype'); setExtraParam(bid, 'category'); - setExtraParam(bid, 'subcategory'); } function getPageDimensions() { @@ -1094,8 +1092,6 @@ export const spec = { bidObj.placement = bidReq.params.placement; bidObj.pagetype = bidReq.params.pagetype; bidObj.category = bidReq.params.category; - bidObj.subcategory = bidReq.params.subcategory; - bidObj.environment = bidReq.params.environment; } bidResponses.push(bidObj); }); diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index 9b48caa8233..b804a72fea9 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -17,12 +17,10 @@ Below, the list of Adagio params and where they can be set. | Param name | Global config | AdUnit config | | ---------- | ------------- | ------------- | | siteId | x | -| organizationId (obsolete) | | x -| site (obsolete) | | x +| organizationId * | | x +| site * | | x | pagetype | x | x -| environment | x | x | category | x | x -| subcategory | x | x | useAdUnitCodeAsAdUnitElementId | x | x | useAdUnitCodeAsPlacement | x | x | placement | | x @@ -31,6 +29,8 @@ Below, the list of Adagio params and where they can be set. | video | | x | native | | x +_* These params are deprecated in favor the Global configuration setup, see below._ + ### Global configuration The global configuration is used to store params once instead of duplicate them to each adUnit. The values will be used as "params" in the ad-request. @@ -49,9 +49,7 @@ pbjs.setConfig({ // - underscores `_` // Also, each param can have at most 50 unique active values (case-insensitive). pagetype: 'article', // Highly recommended. The pagetype describes what kind of content will be present in the page. - environment: 'mobile', // Recommended. Environment where the page is displayed. category: 'sport', // Recommended. Category of the content displayed in the page. - subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. useAdUnitCodeAsAdUnitElementId: false, // Optional. Use it by-pass adUnitElementId and use the adUnit code as value useAdUnitCodeAsPlacement: false, // Optional. Use it to by-pass placement and use the adUnit code as value }, @@ -62,9 +60,7 @@ pbjs.setConfig({ Adagio will use FPD data as fallback for the params below: - pagetype -- environment - category -- subcategory If the FPD value is an array, the 1st value of this array will be used. @@ -107,9 +103,7 @@ var adUnits = [ debug: true, adagio: { pagetype: 'article', - environment: 'mobile', category: 'sport', - subcategory: 'handball', useAdUnitCodeAsAdUnitElementId: false, useAdUnitCodeAsPlacement: false, } @@ -208,12 +202,6 @@ var adUnits = [ return bidResponse.site; } }, - { - key: "environment", - val: function (bidResponse) { - return bidResponse.environment; - } - }, { key: "placement", val: function (bidResponse) { @@ -231,12 +219,6 @@ var adUnits = [ val: function (bidResponse) { return bidResponse.category; } - }, - { - key: "subcategory", - val: function (bidResponse) { - return bidResponse.subcategory; - } } ] } diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 436d481c4a1..8abdc621922 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -149,10 +149,8 @@ describe('Adagio bid adapter', () => { site: { ext: { data: { - environment: 'desktop', pagetype: 'abc', - category: ['cat1', 'cat2', 'cat3'], - subcategory: [] + category: ['cat1', 'cat2', 'cat3'] } } } @@ -167,17 +165,11 @@ describe('Adagio bid adapter', () => { return utils.deepAccess(config, key); }); - setExtraParam(bid, 'environment'); - expect(bid.params.environment).to.equal('desktop'); - setExtraParam(bid, 'pagetype') expect(bid.params.pagetype).to.equal('article'); setExtraParam(bid, 'category'); expect(bid.params.category).to.equal('cat1'); // Only the first value is kept - - setExtraParam(bid, 'subcategory'); - expect(bid.params.subcategory).to.be.undefined; }); it('should use the adUnit param unit if defined', function() { @@ -784,8 +776,6 @@ describe('Adagio bid adapter', () => { adUnitElementId: 'gpt-adunit-code', pagetype: 'ARTICLE', category: 'NEWS', - subcategory: 'SPORT', - environment: 'desktop', supportIObs: true }, adUnitCode: 'adunit-code', @@ -833,8 +823,6 @@ describe('Adagio bid adapter', () => { site: 'SITE-NAME', pagetype: 'ARTICLE', category: 'NEWS', - subcategory: 'SPORT', - environment: 'desktop', aDomain: ['advertiser.com'], mediaType: 'banner', meta: { @@ -868,8 +856,6 @@ describe('Adagio bid adapter', () => { site: 'SITE-NAME', pagetype: 'ARTICLE', category: 'NEWS', - subcategory: 'SPORT', - environment: 'desktop', aDomain: ['advertiser.com'], mediaType: 'banner', meta: { @@ -1392,19 +1378,6 @@ describe('Adagio bid adapter', () => { }); describe.skip('optional params auto detection', function() { - it('should auto detect environment', function() { - const getDeviceStub = sandbox.stub(_features, 'getDevice'); - - getDeviceStub.returns(5); - expect(adagio.autoDetectEnvironment()).to.eq('tablet'); - - getDeviceStub.returns(4); - expect(adagio.autoDetectEnvironment()).to.eq('mobile'); - - getDeviceStub.returns(2); - expect(adagio.autoDetectEnvironment()).to.eq('desktop'); - }); - it('should auto detect adUnitElementId when GPT is used', function() { sandbox.stub(utils, 'getGptSlotInfoForAdUnitCode').withArgs('banner').returns({divId: 'gpt-banner'}); expect(adagio.autoDetectAdUnitElementId('banner')).to.eq('gpt-banner'); From 408221b84507cb130de9ef647f021f5f75d05070 Mon Sep 17 00:00:00 2001 From: couchcrew-thomas Date: Thu, 5 Jan 2023 17:34:58 +0100 Subject: [PATCH 566/569] Feedad Bid Adapter: added new bid request parameters (#9397) * added file scaffold * added isBidRequestValid implementation * added local prototype of ad integration * added implementation for placement ID validation * fixed video context filter * applied lint to feedad bid adapter * added unit test for bid request validation * added buildRequest unit test * added unit tests for timeout and bid won callbacks * updated bid request to FeedAd API * added parsing of feedad api bid response * added transmisison of tracking events to FeedAd Api * code cleanup * updated feedad unit tests for buildRequest method * added unit tests for event tracking implementation * added unit test for interpretResponse method * added adapter documentation * added dedicated feedad example page * updated feedad adapter to use live system * updated FeedAd adapter placement ID regex * removed groups from FeedAd adapter placement ID regex * removed dedicated feedad example page * updated imports in FeedAd adapter file to use relative paths * updated FeedAd adapter unit test to use sinon.useFakeXMLHttpRequest() * added GDPR fields to the FeedAd bid request * removed video from supported media types of the FeedAd adapter * increased version code of FeedAd adapter to 1.0.2 * removed unnecessary check of bidder request * fixed unit test testing for old FeedAd version * removed video media type example from documentation file * added gvlid to FeedAd adapter * added decoration parameter to adapter documentation * added pass through of additional bid parameters * added user syncs to FeedAd bid adapter * increased FeedAd bid adapter version * lint pass over FeedAd bid adapter * fixed parsing of user syncs from server response * increased FeedAd bid adapter version * fixed version code in test file * added adapter and prebid version to bid request parameters * removed TODO item * added missing test case for user syncs * increased adapter version to 1.0.5 --- modules/feedadBidAdapter.js | 12 ++++++----- test/spec/modules/feedadBidAdapter_spec.js | 24 +++++++++++++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/modules/feedadBidAdapter.js b/modules/feedadBidAdapter.js index ef2e57c553f..7b684efab3c 100644 --- a/modules/feedadBidAdapter.js +++ b/modules/feedadBidAdapter.js @@ -7,7 +7,7 @@ import {ajax} from '../src/ajax.js'; * Version of the FeedAd bid adapter * @type {string} */ -const VERSION = '1.0.4'; +const VERSION = '1.0.5'; /** * @typedef {object} FeedAdApiBidRequest @@ -16,7 +16,8 @@ const VERSION = '1.0.4'; * @property {number} ad_type * @property {string} client_token * @property {string} placement_id - * @property {string} sdk_version + * @property {string} prebid_adapter_version + * @property {string} prebid_sdk_version * @property {boolean} app_hybrid * * @property {string} [app_bundle_id] @@ -181,7 +182,8 @@ function createApiBidRParams(request) { ad_type: 0, client_token: request.params.clientToken, placement_id: request.params.placementId, - sdk_version: `prebid_${VERSION}`, + prebid_adapter_version: VERSION, + prebid_sdk_version: '$prebid.version$', app_hybrid: false, }); } @@ -207,7 +209,6 @@ function buildRequests(validBidRequests, bidderRequest) { }) }); data.bids.forEach(bid => BID_METADATA[bid.bidId] = { - // TODO: is 'page' the right value here? referer: data.refererInfo.page, transactionId: bid.transactionId }); @@ -266,7 +267,8 @@ function createTrackingParams(data, klass) { prebid_bid_id: bidId, prebid_transaction_id: transactionId, referer, - sdk_version: VERSION + prebid_adapter_version: VERSION, + prebid_sdk_version: '$prebid.version$', }; } diff --git a/test/spec/modules/feedadBidAdapter_spec.js b/test/spec/modules/feedadBidAdapter_spec.js index 6aed670a563..8cbd6907890 100644 --- a/test/spec/modules/feedadBidAdapter_spec.js +++ b/test/spec/modules/feedadBidAdapter_spec.js @@ -4,6 +4,7 @@ import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; import {server} from 'test/mocks/xhr.js'; const CODE = 'feedad'; +const EXPECTED_ADAPTER_VERSION = '1.0.5'; describe('FeedAdAdapter', function () { describe('Public API', function () { @@ -300,6 +301,20 @@ describe('FeedAdAdapter', function () { expect(result.data.gdprApplies).to.equal(request.gdprConsent.gdprApplies); expect(result.data.consentIabTcf).to.equal(request.gdprConsent.consentString); }); + it('should include adapter and prebid version', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.data.bids[0].params.prebid_adapter_version).to.equal(EXPECTED_ADAPTER_VERSION); + expect(result.data.bids[0].params.prebid_sdk_version).to.equal('$prebid.version$'); + }); }); describe('interpretResponse', function () { @@ -482,6 +497,12 @@ describe('FeedAdAdapter', function () { expect(() => spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, it)).not.to.throw; }); }); + + it('should return empty array if the body extension is null', function () { + const response = mockServerResponse({ext: null}); + const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, response); + expect(result).to.deep.equal([]); + }); }); describe('event tracking calls', function () { @@ -617,7 +638,8 @@ describe('FeedAdAdapter', function () { prebid_bid_id: bidId, prebid_transaction_id: transactionId, referer, - sdk_version: '1.0.4' + prebid_adapter_version: EXPECTED_ADAPTER_VERSION, + prebid_sdk_version: '$prebid.version$', }; subject(data); expect(server.requests.length).to.equal(1); From 768c1d3c59e855db01d6135efab220cbdc749b39 Mon Sep 17 00:00:00 2001 From: Christoph <29540638+kippsterr@users.noreply.github.com> Date: Fri, 6 Jan 2023 13:07:51 +0100 Subject: [PATCH 567/569] Yieldlab Bid Adapter: code style updates (#9386) * Consistently add trailing comma and semicolons everywhere * Use shorthand object property function definition * Fix typo and update type hint --- modules/yieldlabBidAdapter.js | 270 ++++---- test/spec/modules/yieldlabBidAdapter_spec.js | 610 +++++++++---------- 2 files changed, 440 insertions(+), 440 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 6e4f6644140..cadeb9c1300 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -1,17 +1,17 @@ -import { _each, deepAccess, isArray, isFn, isPlainObject, timestamp } from '../src/utils.js' -import { registerBidder } from '../src/adapters/bidderFactory.js' -import { find } from '../src/polyfill.js' -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js' -import { Renderer } from '../src/Renderer.js' +import { _each, deepAccess, isArray, isFn, isPlainObject, timestamp } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { find } from '../src/polyfill.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -const ENDPOINT = 'https://ad.yieldlab.net' -const BIDDER_CODE = 'yieldlab' -const BID_RESPONSE_TTL_SEC = 300 -const CURRENCY_CODE = 'EUR' -const OUTSTREAMPLAYER_URL = 'https://ad.adition.com/dynamic.ad?a=o193092&ma_loadEvent=ma-start-event' -const GVLID = 70 -const DIMENSION_SIGN = 'x' +const ENDPOINT = 'https://ad.yieldlab.net'; +const BIDDER_CODE = 'yieldlab'; +const BID_RESPONSE_TTL_SEC = 300; +const CURRENCY_CODE = 'EUR'; +const OUTSTREAMPLAYER_URL = 'https://ad.adition.com/dynamic.ad?a=o193092&ma_loadEvent=ma-start-event'; +const GVLID = 70; +const DIMENSION_SIGN = 'x'; export const spec = { code: BIDDER_CODE, @@ -22,11 +22,11 @@ export const spec = { * @param {object} bid * @returns {boolean} */ - isBidRequestValid: function (bid) { + isBidRequestValid(bid) { if (bid && bid.params && bid.params.adslotId && bid.params.supplyId) { - return true + return true; } - return false + return false; }, /** @@ -35,85 +35,85 @@ export const spec = { * @param [bidderRequest] * @returns {ServerRequest|ServerRequest[]} */ - buildRequests: function (validBidRequests, bidderRequest) { + buildRequests(validBidRequests, bidderRequest) { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - const adslotIds = [] + const adslotIds = []; const adslotSizes = []; const adslotFloors = []; - const timestamp = Date.now() + const timestamp = Date.now(); const query = { ts: timestamp, - json: true - } + json: true, + }; _each(validBidRequests, function (bid) { - adslotIds.push(bid.params.adslotId) - const sizes = extractSizes(bid) + adslotIds.push(bid.params.adslotId); + const sizes = extractSizes(bid); if (sizes.length > 0) { - adslotSizes.push(bid.params.adslotId + ':' + sizes.join('|')) + adslotSizes.push(bid.params.adslotId + ':' + sizes.join('|')); } if (bid.params.extId) { query.id = bid.params.extId; } if (bid.params.targeting) { - query.t = createTargetingString(bid.params.targeting) + query.t = createTargetingString(bid.params.targeting); } if (bid.userIdAsEids && Array.isArray(bid.userIdAsEids)) { - query.ids = createUserIdString(bid.userIdAsEids) - query.atypes = createUserIdAtypesString(bid.userIdAsEids) + query.ids = createUserIdString(bid.userIdAsEids); + query.atypes = createUserIdAtypesString(bid.userIdAsEids); } if (bid.params.customParams && isPlainObject(bid.params.customParams)) { for (const prop in bid.params.customParams) { - query[prop] = bid.params.customParams[prop] + query[prop] = bid.params.customParams[prop]; } } if (bid.schain && isPlainObject(bid.schain) && Array.isArray(bid.schain.nodes)) { - query.schain = createSchainString(bid.schain) + query.schain = createSchainString(bid.schain); } - const iabContent = getContentObject(bid) + const iabContent = getContentObject(bid); if (iabContent) { - query.iab_content = createIabContentString(iabContent) + query.iab_content = createIabContentString(iabContent); } - const floor = getBidFloor(bid, sizes) + const floor = getBidFloor(bid, sizes); if (floor) { adslotFloors.push(bid.params.adslotId + ':' + floor); } - }) + }); if (bidderRequest) { if (bidderRequest.refererInfo && bidderRequest.refererInfo.page) { // TODO: is 'page' the right value here? - query.pubref = bidderRequest.refererInfo.page + query.pubref = bidderRequest.refererInfo.page; } if (bidderRequest.gdprConsent) { - query.gdpr = (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true + query.gdpr = (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true; if (query.gdpr) { - query.consent = bidderRequest.gdprConsent.consentString + query.consent = bidderRequest.gdprConsent.consentString; } } } - const adslots = adslotIds.join(',') + const adslots = adslotIds.join(','); if (adslotSizes.length > 0) { - query.sizes = adslotSizes.join(',') + query.sizes = adslotSizes.join(','); } if (adslotFloors.length > 0) { - query.floor = adslotFloors.join(',') + query.floor = adslotFloors.join(','); } - const queryString = createQueryString(query) + const queryString = createQueryString(query); return { method: 'GET', url: `${ENDPOINT}/yp/${adslots}?${queryString}`, validBidRequests: validBidRequests, - queryParams: query - } + queryParams: query, + }; }, /** @@ -122,29 +122,29 @@ export const spec = { * @param {BidRequest} originalBidRequest * @returns {Bid[]} */ - interpretResponse: function (serverResponse, originalBidRequest) { - const bidResponses = [] - const timestamp = Date.now() - const reqParams = originalBidRequest.queryParams + interpretResponse(serverResponse, originalBidRequest) { + const bidResponses = []; + const timestamp = Date.now(); + const reqParams = originalBidRequest.queryParams; originalBidRequest.validBidRequests.forEach(function (bidRequest) { if (!serverResponse.body) { - return + return; } const matchedBid = find(serverResponse.body, function (bidResponse) { - return bidRequest.params.adslotId == bidResponse.id - }) + return bidRequest.params.adslotId == bidResponse.id; + }); if (matchedBid) { - const adUnitSize = bidRequest.sizes.length === 2 && !isArray(bidRequest.sizes[0]) ? bidRequest.sizes : bidRequest.sizes[0] - const adSize = bidRequest.params.adSize !== undefined ? parseSize(bidRequest.params.adSize) : (matchedBid.adsize !== undefined) ? parseSize(matchedBid.adsize) : adUnitSize - const extId = bidRequest.params.extId !== undefined ? '&id=' + bidRequest.params.extId : '' - const adType = matchedBid.adtype !== undefined ? matchedBid.adtype : '' - const gdprApplies = reqParams.gdpr ? '&gdpr=' + reqParams.gdpr : '' - const gdprConsent = reqParams.consent ? '&consent=' + reqParams.consent : '' - const pvId = matchedBid.pvid !== undefined ? '&pvid=' + matchedBid.pvid : '' - const iabContent = reqParams.iab_content ? '&iab_content=' + reqParams.iab_content : '' + const adUnitSize = bidRequest.sizes.length === 2 && !isArray(bidRequest.sizes[0]) ? bidRequest.sizes : bidRequest.sizes[0]; + const adSize = bidRequest.params.adSize !== undefined ? parseSize(bidRequest.params.adSize) : (matchedBid.adsize !== undefined) ? parseSize(matchedBid.adsize) : adUnitSize; + const extId = bidRequest.params.extId !== undefined ? '&id=' + bidRequest.params.extId : ''; + const adType = matchedBid.adtype !== undefined ? matchedBid.adtype : ''; + const gdprApplies = reqParams.gdpr ? '&gdpr=' + reqParams.gdpr : ''; + const gdprConsent = reqParams.consent ? '&consent=' + reqParams.consent : ''; + const pvId = matchedBid.pvid !== undefined ? '&pvid=' + matchedBid.pvid : ''; + const iabContent = reqParams.iab_content ? '&iab_content=' + reqParams.iab_content : ''; const bidResponse = { requestId: bidRequest.bidId, @@ -159,38 +159,38 @@ export const spec = { referrer: '', ad: ``, meta: { - advertiserDomains: (matchedBid.advertiser) ? matchedBid.advertiser : 'n/a' - } - } + advertiserDomains: (matchedBid.advertiser) ? matchedBid.advertiser : 'n/a', + }, + }; if (isVideo(bidRequest, adType)) { - const playersize = getPlayerSize(bidRequest) + const playersize = getPlayerSize(bidRequest); if (playersize) { - bidResponse.width = playersize[0] - bidResponse.height = playersize[1] + bidResponse.width = playersize[0]; + bidResponse.height = playersize[1]; } - bidResponse.mediaType = VIDEO - bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}${iabContent}` + bidResponse.mediaType = VIDEO; + bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}${iabContent}`; if (isOutstream(bidRequest)) { const renderer = Renderer.install({ id: bidRequest.bidId, url: OUTSTREAMPLAYER_URL, - loaded: false - }) - renderer.setRender(outstreamRender) - bidResponse.renderer = renderer + loaded: false, + }); + renderer.setRender(outstreamRender); + bidResponse.renderer = renderer; } } if (isNative(bidRequest, adType)) { // there may be publishers still rely on it - const url = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}` - bidResponse.adUrl = url - bidResponse.mediaType = NATIVE - const nativeImageAssetObj = find(matchedBid.native.assets, e => e.id === 2) + const url = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}`; + bidResponse.adUrl = url; + bidResponse.mediaType = NATIVE; + const nativeImageAssetObj = find(matchedBid.native.assets, e => e.id === 2); const nativeImageAsset = nativeImageAssetObj ? nativeImageAssetObj.img : { url: '', w: 0, h: 0 }; - const nativeTitleAsset = find(matchedBid.native.assets, e => e.id === 1) - const nativeBodyAsset = find(matchedBid.native.assets, e => e.id === 3) + const nativeTitleAsset = find(matchedBid.native.assets, e => e.id === 1); + const nativeBodyAsset = find(matchedBid.native.assets, e => e.id === 3); bidResponse.native = { title: nativeTitleAsset ? nativeTitleAsset.title.text : '', body: nativeBodyAsset ? nativeBodyAsset.data.value : '', @@ -204,10 +204,10 @@ export const spec = { }; } - bidResponses.push(bidResponse) + bidResponses.push(bidResponse); } - }) - return bidResponses + }); + return bidResponses; }, /** @@ -219,13 +219,13 @@ export const spec = { * @param {string} uspConsent Is the US Privacy Consent string. * @return {UserSync[]} The user syncs which should be dropped. */ - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { + getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { const syncs = []; if (syncOptions.iframeEnabled) { const params = []; params.push(`ts=${timestamp()}`); - params.push(`type=h`) + params.push(`type=h`); if (gdprConsent && (typeof gdprConsent.gdprApplies === 'boolean')) { params.push(`gdpr=${Number(gdprConsent.gdprApplies)}`); } @@ -234,12 +234,12 @@ export const spec = { } syncs.push({ type: 'iframe', - url: `${ENDPOINT}/d/6846326/766/2x2?${params.join('&')}` + url: `${ENDPOINT}/d/6846326/766/2x2?${params.join('&')}`, }); } return syncs; - } + }, }; /** @@ -249,7 +249,7 @@ export const spec = { * @returns {Boolean} */ function isVideo(format, adtype) { - return deepAccess(format, 'mediaTypes.video') && adtype.toLowerCase() === 'video' + return deepAccess(format, 'mediaTypes.video') && adtype.toLowerCase() === 'video'; } /** @@ -259,7 +259,7 @@ function isVideo(format, adtype) { * @returns {Boolean} */ function isNative(format, adtype) { - return deepAccess(format, 'mediaTypes.native') && adtype.toLowerCase() === 'native' + return deepAccess(format, 'mediaTypes.native') && adtype.toLowerCase() === 'native'; } /** @@ -268,8 +268,8 @@ function isNative(format, adtype) { * @returns {Boolean} */ function isOutstream(format) { - const context = deepAccess(format, 'mediaTypes.video.context') - return (context === 'outstream') + const context = deepAccess(format, 'mediaTypes.video.context'); + return (context === 'outstream'); } /** @@ -278,30 +278,30 @@ function isOutstream(format) { * @returns {Array} */ function getPlayerSize(format) { - const playerSize = deepAccess(format, 'mediaTypes.video.playerSize') - return (playerSize && isArray(playerSize[0])) ? playerSize[0] : playerSize + const playerSize = deepAccess(format, 'mediaTypes.video.playerSize'); + return (playerSize && isArray(playerSize[0])) ? playerSize[0] : playerSize; } /** - * Expands a 'WxH' string as a 2-element [W, H] array + * Expands a 'WxH' string to a 2-element [W, H] array * @param {String} size * @returns {Array} */ function parseSize(size) { - return size.split(DIMENSION_SIGN).map(Number) + return size.split(DIMENSION_SIGN).map(Number); } /** * Creates a string out of an array of eids with source and uid - * @param {Array} eids + * @param {Array.<{source: String, uids: Array.<{id: String, atype: Number, ext: Object}>}>} eids * @returns {String} */ function createUserIdString(eids) { - const str = [] + const str = []; for (let i = 0; i < eids.length; i++) { - str.push(eids[i].source + ':' + eids[i].uids[0].id) + str.push(eids[i].source + ':' + eids[i].uids[0].id); } - return str.join(',') + return str.join(','); } /** @@ -325,18 +325,18 @@ function createUserIdAtypesString(eids) { * @returns {String} */ function createQueryString(obj) { - const str = [] + const str = []; for (const p in obj) { if (obj.hasOwnProperty(p)) { - const val = obj[p] + const val = obj[p]; if (p !== 'schain' && p !== 'iab_content') { - str.push(encodeURIComponent(p) + '=' + encodeURIComponent(val)) + str.push(encodeURIComponent(p) + '=' + encodeURIComponent(val)); } else { - str.push(p + '=' + val) + str.push(p + '=' + val); } } } - return str.join('&') + return str.join('&'); } /** @@ -345,15 +345,15 @@ function createQueryString(obj) { * @returns {String} */ function createTargetingString(obj) { - const str = [] + const str = []; for (const p in obj) { if (obj.hasOwnProperty(p)) { - const key = p - const val = obj[p] - str.push(key + '=' + val) + const key = p; + const val = obj[p]; + str.push(key + '=' + val); } } - return str.join('&') + return str.join('&'); } /** @@ -362,13 +362,13 @@ function createTargetingString(obj) { * @returns {String} */ function createSchainString(schain) { - const ver = schain.ver || '' - const complete = (schain.complete === 1 || schain.complete === 0) ? schain.complete : '' - const keys = ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext'] + const ver = schain.ver || ''; + const complete = (schain.complete === 1 || schain.complete === 0) ? schain.complete : ''; + const keys = ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext']; const nodesString = schain.nodes.reduce((acc, node) => { - return acc += `!${keys.map(key => node[key] ? encodeURIComponentWithBangIncluded(node[key]) : '').join(',')}` - }, '') - return `${ver},${complete}${nodesString}` + return acc += `!${keys.map(key => node[key] ? encodeURIComponentWithBangIncluded(node[key]) : '').join(',')}`; + }, ''); + return `${ver},${complete}${nodesString}`; } /** @@ -380,15 +380,15 @@ function createSchainString(schain) { */ function getContentObject(bid) { if (bid.params.iabContent && isPlainObject(bid.params.iabContent)) { - return bid.params.iabContent + return bid.params.iabContent; } const globalContent = deepAccess(bid, 'ortb2.site') ? deepAccess(bid, 'ortb2.site.content') - : deepAccess(bid, 'ortb2.app.content') + : deepAccess(bid, 'ortb2.app.content'); if (globalContent && isPlainObject(globalContent)) { - return globalContent + return globalContent; } - return undefined + return undefined; } /** @@ -401,15 +401,15 @@ function getContentObject(bid) { * @returns {String} */ function createIabContentString(iabContent) { - const arrKeys = ['keywords', 'cat'] - const str = [] + const arrKeys = ['keywords', 'cat']; + const str = []; const transformObjToParam = (obj = {}, extraKey = '') => { for (const key in obj) { if ((arrKeys.indexOf(key) !== -1 && Array.isArray(obj[key]))) { // Array of defined keyword which have to be joined into one value from "key: [value1, value2, value3]" to "key:value1|value2|value3" - str.push(''.concat(key, ':', obj[key].map(node => encodeURIComponent(node)).join('|'))) + str.push(''.concat(key, ':', obj[key].map(node => encodeURIComponent(node)).join('|'))); } else if (typeof obj[key] !== 'object') { - str.push(''.concat(extraKey + key, ':', encodeURIComponent(obj[key]))) + str.push(''.concat(extraKey + key, ':', encodeURIComponent(obj[key]))); } else { // Object has to be further flattened transformObjToParam(obj[key], ''.concat(extraKey, key, '.')); @@ -417,7 +417,7 @@ function createIabContentString(iabContent) { } return str.join(','); }; - return encodeURIComponent(transformObjToParam(iabContent)) + return encodeURIComponent(transformObjToParam(iabContent)); } /** @@ -426,7 +426,7 @@ function createIabContentString(iabContent) { * @returns {String} */ function encodeURIComponentWithBangIncluded(str) { - return encodeURIComponent(str).replace(/!/g, '%21') + return encodeURIComponent(str).replace(/!/g, '%21'); } /** @@ -435,11 +435,11 @@ function encodeURIComponentWithBangIncluded(str) { */ function outstreamRender(bid) { bid.renderer.push(() => { - window.ma_width = bid.width - window.ma_height = bid.height - window.ma_vastUrl = bid.vastUrl - window.ma_container = bid.adUnitCode - window.document.dispatchEvent(new Event('ma-start-event')) + window.ma_width = bid.width; + window.ma_height = bid.height; + window.ma_vastUrl = bid.vastUrl; + window.ma_container = bid.adUnitCode; + window.document.dispatchEvent(new Event('ma-start-event')); }); } @@ -450,33 +450,33 @@ function outstreamRender(bid) { * @returns {string[]} */ function extractSizes(bid) { - const { mediaTypes } = bid // see https://docs.prebid.org/dev-docs/adunit-reference.html#examples - const sizes = [] + const { mediaTypes } = bid; // see https://docs.prebid.org/dev-docs/adunit-reference.html#examples + const sizes = []; if (isPlainObject(mediaTypes)) { - const { [BANNER]: bannerType } = mediaTypes + const { [BANNER]: bannerType } = mediaTypes; // only applies for multi size Adslots -> BANNER if (bannerType && isArray(bannerType.sizes)) { if (isArray(bannerType.sizes[0])) { // multiple sizes given - sizes.push(bannerType.sizes) + sizes.push(bannerType.sizes); } else { // just one size provided as array -> wrap to uniformly flatten later - sizes.push([bannerType.sizes]) + sizes.push([bannerType.sizes]); } } // The bid top level field `sizes` is deprecated and should not be used anymore. Keeping it for compatibility. } else if (isArray(bid.sizes)) { if (isArray(bid.sizes[0])) { - sizes.push(bid.sizes) + sizes.push(bid.sizes); } else { - sizes.push([bid.sizes]) + sizes.push([bid.sizes]); } } /** @type {Set} */ - const deduplicatedSizeStrings = new Set(sizes.flat().map(([width, height]) => width + DIMENSION_SIGN + height)) + const deduplicatedSizeStrings = new Set(sizes.flat().map(([width, height]) => width + DIMENSION_SIGN + height)); - return Array.from(deduplicatedSizeStrings) + return Array.from(deduplicatedSizeStrings); } /** @@ -497,7 +497,7 @@ function getBidFloor(bid, sizes) { const floor = bid.getFloor({ currency: CURRENCY_CODE, mediaType: mediaType !== undefined && spec.supportedMediaTypes.includes(mediaType) ? mediaType : '*', - size: sizes.length !== 1 ? '*' : sizes[0].split(DIMENSION_SIGN) + size: sizes.length !== 1 ? '*' : sizes[0].split(DIMENSION_SIGN), }); if (floor.currency === CURRENCY_CODE) { return (floor.floor * 100).toFixed(0); @@ -505,4 +505,4 @@ function getBidFloor(bid, sizes) { return undefined; } -registerBidder(spec) +registerBidder(spec); diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 07d42df1319..e5151cf789c 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -1,7 +1,7 @@ import { config } from 'src/config.js'; -import { expect } from 'chai' -import { spec } from 'modules/yieldlabBidAdapter.js' -import { newBidder } from 'src/adapters/bidderFactory.js' +import { expect } from 'chai'; +import { spec } from 'modules/yieldlabBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; const DEFAULT_REQUEST = () => ({ bidder: 'yieldlab', @@ -11,11 +11,11 @@ const DEFAULT_REQUEST = () => ({ targeting: { key1: 'value1', key2: 'value2', - notDoubleEncoded: 'value3,value4' + notDoubleEncoded: 'value3,value4', }, customParams: { extraParam: true, - foo: 'bar' + foo: 'bar', }, extId: 'abc', iabContent: { @@ -31,8 +31,8 @@ const DEFAULT_REQUEST = () => ({ cat: ['cat1', 'cat2,ppp', 'cat3|||//'], context: '7', keywords: ['k1,', 'k2..'], - live: '0' - } + live: '0', + }, }, bidderRequestId: '143346cf0f1731', auctionId: '2e41f65424c87c', @@ -43,14 +43,14 @@ const DEFAULT_REQUEST = () => ({ source: 'netid.de', uids: [{ id: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', - atype: 1 - }] + atype: 1, + }], }, { source: 'digitrust.de', uids: [{ id: 'd8aa10fa-d86c-451d-aad8-5f16162a9e64', - atype: 2 - }] + atype: 2, + }], }], schain: { ver: '1.0', @@ -59,32 +59,32 @@ const DEFAULT_REQUEST = () => ({ { asi: 'indirectseller.com', sid: '1', - hp: 1 + hp: 1, }, { asi: 'indirectseller2.com', name: 'indirectseller2 name with comma , and bang !', sid: '2', - hp: 1 - } - ] - } -}) + hp: 1, + }, + ], + }, +}); const VIDEO_REQUEST = () => Object.assign(DEFAULT_REQUEST(), { mediaTypes: { video: { playerSize: [[640, 480]], - context: 'instream' - } - } -}) + context: 'instream', + }, + }, +}); const NATIVE_REQUEST = () => Object.assign(DEFAULT_REQUEST(), { mediaTypes: { - native: {} - } -}) + native: {}, + }, +}); const IAB_REQUEST = () => Object.assign(DEFAULT_REQUEST(), { params: { @@ -119,7 +119,7 @@ const IAB_REQUEST = () => Object.assign(DEFAULT_REQUEST(), { name: 'bar', cattax: 532, cat: [1, 'foo', true], - domain: 'producer.test' + domain: 'producer.test', }, data: { id: 'foo', @@ -129,8 +129,8 @@ const IAB_REQUEST = () => Object.assign(DEFAULT_REQUEST(), { value: 'bar', ext: { foo: { - bar: 'bar' - } + bar: 'bar', + }, }, }, { name: 'foo2', @@ -139,27 +139,27 @@ const IAB_REQUEST = () => Object.assign(DEFAULT_REQUEST(), { test: { nums: { int: 123, - float: 123.123 + float: 123.123, }, bool: true, - string: 'foo2' - } - } + string: 'foo2', + }, + }, }], }, network: { id: 'foo', name: 'bar', - domain: 'network.test' + domain: 'network.test', }, channel: { id: 'bar', name: 'foo', - domain: 'channel.test' - } - } - } -}) + domain: 'channel.test', + }, + }, + }, +}); const RESPONSE = { advertiser: 'yieldlab', @@ -169,173 +169,173 @@ const RESPONSE = { price: 1, pid: 2222, adsize: '728x90', - adtype: 'BANNER' -} + adtype: 'BANNER', +}; const NATIVE_RESPONSE = Object.assign({}, RESPONSE, { adtype: 'NATIVE', native: { link: { - url: 'https://www.yieldlab.de' + url: 'https://www.yieldlab.de', }, assets: [ { id: 1, title: { - text: 'This is a great headline' - } + text: 'This is a great headline', + }, }, { id: 2, img: { url: 'https://localhost:8080/yl-logo100x100.jpg', w: 100, - h: 100 - } + h: 100, + }, }, { id: 3, data: { - value: 'Native body value' - } - } + value: 'Native body value', + }, + }, ], imptrackers: [ 'http://localhost:8080/ve?d=ODE9ZSY2MTI1MjAzNjMzMzYxPXN0JjA0NWUwZDk0NTY5Yi05M2FiLWUwZTQtOWFjNy1hYWY0MzFiZj1kaXQmMj12', 'http://localhost:8080/md/1111/9efa4e76-2030-4f04-bb9f-322541f8d611?mdata=false&pvid=false&ids=x:1', - 'http://localhost:8080/imp?s=13216&d=2171514&a=12548955&ts=1633363025216&tid=fb134faa-7ca9-4e0e-ba39-b96549d0e540&l=0' - ] - } -}) + 'http://localhost:8080/imp?s=13216&d=2171514&a=12548955&ts=1633363025216&tid=fb134faa-7ca9-4e0e-ba39-b96549d0e540&l=0', + ], + }, +}); const VIDEO_RESPONSE = Object.assign({}, RESPONSE, { - adtype: 'VIDEO' -}) + adtype: 'VIDEO', +}); const PVID_RESPONSE = Object.assign({}, VIDEO_RESPONSE, { - pvid: '43513f11-55a0-4a83-94e5-0ebc08f54a2c' -}) + pvid: '43513f11-55a0-4a83-94e5-0ebc08f54a2c', +}); const REQPARAMS = { json: true, - ts: 1234567890 -} + ts: 1234567890, +}; const REQPARAMS_GDPR = Object.assign({}, REQPARAMS, { gdpr: true, - consent: 'BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA' -}) + consent: 'BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA', +}); const REQPARAMS_IAB_CONTENT = Object.assign({}, REQPARAMS, { - iab_content: 'id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0' -}) + iab_content: 'id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0', +}); describe('yieldlabBidAdapter', () => { describe('instantiation from spec', () => { it('is working properly', () => { - const yieldlabBidAdapter = newBidder(spec) - expect(yieldlabBidAdapter.callBids).to.exist.and.to.be.a('function') - }) - }) + const yieldlabBidAdapter = newBidder(spec); + expect(yieldlabBidAdapter.callBids).to.exist.and.to.be.a('function'); + }); + }); describe('isBidRequestValid', () => { it('should return true when all required parameters are found', () => { const request = { params: { adslotId: '1111', - supplyId: '2222' - } - } - expect(spec.isBidRequestValid(request)).to.equal(true) - }) + supplyId: '2222', + }, + }; + expect(spec.isBidRequestValid(request)).to.equal(true); + }); it('should return false when required parameters are missing', () => { - expect(spec.isBidRequestValid({})).to.equal(false) - }) - }) + expect(spec.isBidRequestValid({})).to.equal(false); + }); + }); describe('buildRequests', () => { - const bidRequests = [DEFAULT_REQUEST()] + const bidRequests = [DEFAULT_REQUEST()]; describe('default functionality', () => { - let request + let request; before(() => { - request = spec.buildRequests(bidRequests) - }) + request = spec.buildRequests(bidRequests); + }); it('sends bid request to ENDPOINT via GET', () => { - expect(request.method).to.equal('GET') - }) + expect(request.method).to.equal('GET'); + }); it('returns a list of valid requests', () => { - expect(request.validBidRequests).to.eql(bidRequests) - }) + expect(request.validBidRequests).to.eql(bidRequests); + }); it('passes single-encoded targeting to bid request', () => { - expect(request.url).to.include('t=key1%3Dvalue1%26key2%3Dvalue2%26notDoubleEncoded%3Dvalue3%2Cvalue4') - }) + expect(request.url).to.include('t=key1%3Dvalue1%26key2%3Dvalue2%26notDoubleEncoded%3Dvalue3%2Cvalue4'); + }); it('passes userids to bid request', () => { - expect(request.url).to.include('ids=netid.de%3AfH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg%2Cdigitrust.de%3Ad8aa10fa-d86c-451d-aad8-5f16162a9e64') - }) + expect(request.url).to.include('ids=netid.de%3AfH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg%2Cdigitrust.de%3Ad8aa10fa-d86c-451d-aad8-5f16162a9e64'); + }); it('passes atype to bid request', () => { - expect(request.url).to.include('atypes=netid.de%3A1%2Cdigitrust.de%3A2') - }) + expect(request.url).to.include('atypes=netid.de%3A1%2Cdigitrust.de%3A2'); + }); it('passes extra params to bid request', () => { - expect(request.url).to.include('extraParam=true&foo=bar') - }) + expect(request.url).to.include('extraParam=true&foo=bar'); + }); it('passes unencoded schain string to bid request', () => { - expect(request.url).to.include('schain=1.0,1!indirectseller.com,1,1,,,,!indirectseller2.com,2,1,,indirectseller2%20name%20with%20comma%20%2C%20and%20bang%20%21,,') - }) + expect(request.url).to.include('schain=1.0,1!indirectseller.com,1,1,,,,!indirectseller2.com,2,1,,indirectseller2%20name%20with%20comma%20%2C%20and%20bang%20%21,,'); + }); it('passes iab_content string to bid request', () => { - expect(request.url).to.include('iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0') - }) + expect(request.url).to.include('iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0'); + }); it('passes correct size to bid request', () => { - expect(request.url).to.include('728x90') - }) + expect(request.url).to.include('728x90'); + }); it('passes external id to bid request', () => { - expect(request.url).to.include('id=abc') - }) - }) + expect(request.url).to.include('id=abc'); + }); + }); describe('iab_content handling', () => { const siteConfig = { ortb2: { site: { content: { - id: 'id_from_config' - } - } - } - } + id: 'id_from_config', + }, + }, + }, + }; beforeEach(() => { - config.setConfig(siteConfig) - }) + config.setConfig(siteConfig); + }); afterEach(() => { - config.resetConfig() - }) + config.resetConfig(); + }); it('generates iab_content string from bidder params', () => { - const request = spec.buildRequests(bidRequests) - expect(request.url).to.include('iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0') - }) + const request = spec.buildRequests(bidRequests); + expect(request.url).to.include('iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0'); + }); it('generates iab_content string from first party data if not provided in bidder params', () => { - const requestWithoutIabContent = DEFAULT_REQUEST() - delete requestWithoutIabContent.params.iabContent + const requestWithoutIabContent = DEFAULT_REQUEST(); + delete requestWithoutIabContent.params.iabContent; - const request = spec.buildRequests([{...requestWithoutIabContent, ...siteConfig}]) - expect(request.url).to.include('iab_content=id%3Aid_from_config') - }) + const request = spec.buildRequests([{...requestWithoutIabContent, ...siteConfig}]); + expect(request.url).to.include('iab_content=id%3Aid_from_config'); + }); it('flattens the iabContent, encodes the values, joins the keywords into one value, and than encodes the iab_content request param ', () => { const expectedIabContentValue = encodeURIComponent( @@ -382,18 +382,18 @@ describe('yieldlabBidAdapter', () => { 'channel.id:bar,' + 'channel.name:foo,' + 'channel.domain:channel.test' - ) - const request = spec.buildRequests([IAB_REQUEST()], REQPARAMS) - expect(request.url).to.include('iab_content=' + expectedIabContentValue) - }) - }) + ); + const request = spec.buildRequests([IAB_REQUEST()], REQPARAMS); + expect(request.url).to.include('iab_content=' + expectedIabContentValue); + }); + }); it('passes unencoded schain string to bid request when complete == 0', () => { - const schainRequest = DEFAULT_REQUEST() + const schainRequest = DEFAULT_REQUEST(); schainRequest.schain.complete = 0; // - const request = spec.buildRequests([schainRequest]) - expect(request.url).to.include('schain=1.0,0!indirectseller.com,1,1,,,,!indirectseller2.com,2,1,,indirectseller2%20name%20with%20comma%20%2C%20and%20bang%20%21,,') - }) + const request = spec.buildRequests([schainRequest]); + expect(request.url).to.include('schain=1.0,0!indirectseller.com,1,1,,,,!indirectseller2.com,2,1,,indirectseller2%20name%20with%20comma%20%2C%20and%20bang%20%21,,'); + }); it('passes encoded referer to bid request', () => { const refererRequest = spec.buildRequests(bidRequests, { @@ -402,123 +402,123 @@ describe('yieldlabBidAdapter', () => { numIframes: 0, reachedTop: true, page: 'https://www.yieldlab.de/test?with=querystring', - stack: ['https://www.yieldlab.de/test?with=querystring'] - } - }) + stack: ['https://www.yieldlab.de/test?with=querystring'], + }, + }); - expect(refererRequest.url).to.include('pubref=https%3A%2F%2Fwww.yieldlab.de%2Ftest%3Fwith%3Dquerystring') - }) + expect(refererRequest.url).to.include('pubref=https%3A%2F%2Fwww.yieldlab.de%2Ftest%3Fwith%3Dquerystring'); + }); it('passes gdpr flag and consent if present', () => { const gdprRequest = spec.buildRequests(bidRequests, { gdprConsent: { consentString: 'BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA', - gdprApplies: true - } - }) + gdprApplies: true, + }, + }); - expect(gdprRequest.url).to.include('consent=BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA') - expect(gdprRequest.url).to.include('gdpr=true') - }) + expect(gdprRequest.url).to.include('consent=BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA'); + expect(gdprRequest.url).to.include('gdpr=true'); + }); describe('sizes handling', () => { it('passes correct size to bid request for mediaType banner', () => { const bannerRequest = DEFAULT_REQUEST(); bannerRequest.mediaTypes = { banner: { - sizes: [[123, 456]] - } - } + sizes: [[123, 456]], + }, + }; // when mediaTypes is present it has precedence over the sizes field (728, 90) - let request = spec.buildRequests([bannerRequest], REQPARAMS) - expect(request.url).to.include('sizes') - expect(request.url).to.include('123x456') - - bannerRequest.mediaTypes.banner.sizes = [123, 456] - request = spec.buildRequests([bannerRequest], REQPARAMS) - expect(request.url).to.include('123x456') - - bannerRequest.mediaTypes.banner.sizes = [[123, 456], [320, 240]] - request = spec.buildRequests([bannerRequest], REQPARAMS) - expect(request.url).to.include('123x456') - expect(request.url).to.include('320x240') - }) + let request = spec.buildRequests([bannerRequest], REQPARAMS); + expect(request.url).to.include('sizes'); + expect(request.url).to.include('123x456'); + + bannerRequest.mediaTypes.banner.sizes = [123, 456]; + request = spec.buildRequests([bannerRequest], REQPARAMS); + expect(request.url).to.include('123x456'); + + bannerRequest.mediaTypes.banner.sizes = [[123, 456], [320, 240]]; + request = spec.buildRequests([bannerRequest], REQPARAMS); + expect(request.url).to.include('123x456'); + expect(request.url).to.include('320x240'); + }); it('passes correct sizes to bid request when mediaType is not present', () => { // information is taken from the top level sizes field const sizesRequest = DEFAULT_REQUEST(); - let request = spec.buildRequests([sizesRequest], REQPARAMS) - expect(request.url).to.include('sizes') - expect(request.url).to.include('728x90') + let request = spec.buildRequests([sizesRequest], REQPARAMS); + expect(request.url).to.include('sizes'); + expect(request.url).to.include('728x90'); - sizesRequest.sizes = [[728, 90]] - request = spec.buildRequests([sizesRequest], REQPARAMS) - expect(request.url).to.include('728x90') + sizesRequest.sizes = [[728, 90]]; + request = spec.buildRequests([sizesRequest], REQPARAMS); + expect(request.url).to.include('728x90'); - sizesRequest.sizes = [[728, 90], [320, 240]] - request = spec.buildRequests([sizesRequest], REQPARAMS) - expect(request.url).to.include('728x90') - }) + sizesRequest.sizes = [[728, 90], [320, 240]]; + request = spec.buildRequests([sizesRequest], REQPARAMS); + expect(request.url).to.include('728x90'); + }); it('does not pass the sizes parameter for mediaType video', () => { const videoRequest = VIDEO_REQUEST(); - let request = spec.buildRequests([videoRequest], REQPARAMS) - expect(request.url).to.not.include('sizes') - }) + let request = spec.buildRequests([videoRequest], REQPARAMS); + expect(request.url).to.not.include('sizes'); + }); it('does not pass the sizes parameter for mediaType native', () => { const nativeRequest = NATIVE_REQUEST(); - let request = spec.buildRequests([nativeRequest], REQPARAMS) - expect(request.url).to.not.include('sizes') - }) - }) - }) + let request = spec.buildRequests([nativeRequest], REQPARAMS); + expect(request.url).to.not.include('sizes'); + }); + }); + }); describe('interpretResponse', () => { - let bidRequest + let bidRequest; before(() => { - bidRequest = DEFAULT_REQUEST() - }) + bidRequest = DEFAULT_REQUEST(); + }); it('handles nobid responses', () => { - expect(spec.interpretResponse({body: {}}, {validBidRequests: []}).length).to.equal(0) - expect(spec.interpretResponse({body: []}, {validBidRequests: []}).length).to.equal(0) - }) + expect(spec.interpretResponse({body: {}}, {validBidRequests: []}).length).to.equal(0); + expect(spec.interpretResponse({body: []}, {validBidRequests: []}).length).to.equal(0); + }); it('should get correct bid response', () => { - const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [bidRequest], queryParams: REQPARAMS}) - - expect(result[0].requestId).to.equal('2d925f27f5079f') - expect(result[0].cpm).to.equal(0.01) - expect(result[0].width).to.equal(728) - expect(result[0].height).to.equal(90) - expect(result[0].creativeId).to.equal('1111') - expect(result[0].dealId).to.equal(2222) - expect(result[0].currency).to.equal('EUR') - expect(result[0].netRevenue).to.equal(false) - expect(result[0].ttl).to.equal(300) - expect(result[0].referrer).to.equal('') - expect(result[0].meta.advertiserDomains).to.equal('yieldlab') - expect(result[0].ad).to.include('